Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: The OGRLinearRing geometry class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "ogr_geometry.h"
16 :
17 : #include <climits>
18 : #include <cmath>
19 : #include <cstring>
20 : #include <limits>
21 :
22 : #include "cpl_error.h"
23 : #include "ogr_core.h"
24 : #include "ogr_geometry.h"
25 : #include "ogr_p.h"
26 :
27 : /************************************************************************/
28 : /* OGRLinearRing( const OGRLinearRing& ) */
29 : /************************************************************************/
30 :
31 : /**
32 : * \brief Copy constructor.
33 : */
34 :
35 : OGRLinearRing::OGRLinearRing(const OGRLinearRing &) = default;
36 :
37 : /************************************************************************/
38 : /* OGRLinearRing() */
39 : /************************************************************************/
40 :
41 : /** Constructor
42 : * @param poSrcRing source ring.
43 : */
44 0 : OGRLinearRing::OGRLinearRing(const OGRLinearRing *poSrcRing)
45 :
46 : {
47 0 : if (poSrcRing == nullptr)
48 : {
49 0 : CPLDebug("OGR",
50 : "OGRLinearRing::OGRLinearRing(OGRLinearRing*poSrcRing) - "
51 : "passed in ring is NULL!");
52 0 : return;
53 : }
54 :
55 0 : setNumPoints(poSrcRing->getNumPoints(), FALSE);
56 :
57 0 : memcpy(paoPoints, poSrcRing->paoPoints,
58 0 : sizeof(OGRRawPoint) * getNumPoints());
59 :
60 0 : if (poSrcRing->padfZ)
61 : {
62 0 : Make3D();
63 0 : memcpy(padfZ, poSrcRing->padfZ, sizeof(double) * getNumPoints());
64 : }
65 : }
66 :
67 : /************************************************************************/
68 : /* operator=( const OGRLinearRing& ) */
69 : /************************************************************************/
70 :
71 : /**
72 : * \brief Assignment operator.
73 : */
74 :
75 5 : OGRLinearRing &OGRLinearRing::operator=(const OGRLinearRing &other)
76 : {
77 5 : if (this != &other)
78 : {
79 4 : OGRLineString::operator=(other);
80 : }
81 5 : return *this;
82 : }
83 :
84 : /************************************************************************/
85 : /* getGeometryName() */
86 : /************************************************************************/
87 :
88 214362 : const char *OGRLinearRing::getGeometryName() const
89 :
90 : {
91 214362 : return "LINEARRING";
92 : }
93 :
94 : /************************************************************************/
95 : /* WkbSize() */
96 : /* */
97 : /* Disable this method. */
98 : /************************************************************************/
99 :
100 62841 : size_t OGRLinearRing::WkbSize() const
101 :
102 : {
103 62841 : return 0;
104 : }
105 :
106 : /************************************************************************/
107 : /* importFromWkb() */
108 : /* */
109 : /* Disable method for this class. */
110 : /************************************************************************/
111 :
112 0 : OGRErr OGRLinearRing::importFromWkb(const unsigned char * /*pabyData*/,
113 : size_t /*nSize*/,
114 : OGRwkbVariant /*eWkbVariant*/,
115 : size_t & /* nBytesConsumedOut */)
116 :
117 : {
118 0 : return OGRERR_UNSUPPORTED_OPERATION;
119 : }
120 :
121 : /************************************************************************/
122 : /* exportToWkb() */
123 : /* */
124 : /* Disable method for this class. */
125 : /************************************************************************/
126 :
127 940 : OGRErr OGRLinearRing::exportToWkb(CPL_UNUSED unsigned char *pabyData,
128 : CPL_UNUSED const OGRwkbExportOptions *) const
129 :
130 : {
131 940 : return OGRERR_UNSUPPORTED_OPERATION;
132 : }
133 :
134 : /************************************************************************/
135 : /* _importFromWkb() */
136 : /* */
137 : /* Helper method for OGRPolygon. NOT A NORMAL importFromWkb() */
138 : /* method. */
139 : /************************************************************************/
140 :
141 : //! @cond Doxygen_Suppress
142 42987 : OGRErr OGRLinearRing::_importFromWkb(OGRwkbByteOrder eByteOrder, int _flags,
143 : const unsigned char *pabyData,
144 : size_t nBytesAvailable,
145 : size_t &nBytesConsumedOut)
146 :
147 : {
148 42987 : nBytesConsumedOut = 0;
149 42987 : if (nBytesAvailable < 4 && nBytesAvailable != static_cast<size_t>(-1))
150 10 : return OGRERR_NOT_ENOUGH_DATA;
151 :
152 : /* -------------------------------------------------------------------- */
153 : /* Get the vertex count. */
154 : /* -------------------------------------------------------------------- */
155 42977 : int nNewNumPoints = 0;
156 :
157 42977 : memcpy(&nNewNumPoints, pabyData, 4);
158 :
159 42977 : if (OGR_SWAP(eByteOrder))
160 138 : nNewNumPoints = CPL_SWAP32(nNewNumPoints);
161 :
162 : // Check if the wkb stream buffer is big enough to store
163 : // fetched number of points.
164 : // 16, 24, or 32 - size of point structure.
165 42977 : size_t nPointSize = 0;
166 42977 : if ((_flags & OGR_G_3D) && (_flags & OGR_G_MEASURED))
167 30304 : nPointSize = 32;
168 12673 : else if ((_flags & OGR_G_3D) || (_flags & OGR_G_MEASURED))
169 2578 : nPointSize = 24;
170 : else
171 10095 : nPointSize = 16;
172 :
173 85905 : if (nNewNumPoints < 0 ||
174 42927 : static_cast<size_t>(nNewNumPoints) >
175 42927 : std::numeric_limits<size_t>::max() / nPointSize)
176 : {
177 49 : return OGRERR_CORRUPT_DATA;
178 : }
179 42929 : const size_t nBufferMinSize = nPointSize * nNewNumPoints;
180 42929 : if (nBytesAvailable != static_cast<size_t>(-1) &&
181 42914 : nBufferMinSize > nBytesAvailable - 4)
182 : {
183 970 : CPLError(CE_Failure, CPLE_AppDefined,
184 : "Length of input WKB is too small");
185 970 : return OGRERR_NOT_ENOUGH_DATA;
186 : }
187 :
188 : // (Re)Allocation of paoPoints buffer.
189 41959 : setNumPoints(nNewNumPoints, FALSE);
190 :
191 41957 : if (_flags & OGR_G_3D)
192 31752 : Make3D();
193 : else
194 10205 : Make2D();
195 :
196 41957 : if (_flags & OGR_G_MEASURED)
197 29399 : AddM();
198 : else
199 12558 : RemoveM();
200 :
201 41957 : nBytesConsumedOut = 4 + nPointCount * nPointSize;
202 :
203 : /* -------------------------------------------------------------------- */
204 : /* Get the vertices */
205 : /* -------------------------------------------------------------------- */
206 41957 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
207 : {
208 141222 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
209 : {
210 111935 : memcpy(&(paoPoints[i].x), pabyData + 4 + 32 * i, 8);
211 111935 : memcpy(&(paoPoints[i].y), pabyData + 4 + 32 * i + 8, 8);
212 111935 : memcpy(padfZ + i, pabyData + 4 + 32 * i + 16, 8);
213 111935 : memcpy(padfM + i, pabyData + 4 + 32 * i + 24, 8);
214 29287 : }
215 : }
216 12670 : else if (flags & OGR_G_MEASURED)
217 : {
218 619 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
219 : {
220 507 : memcpy(&(paoPoints[i].x), pabyData + 4 + 24 * i, 8);
221 507 : memcpy(&(paoPoints[i].y), pabyData + 4 + 24 * i + 8, 8);
222 507 : memcpy(padfM + i, pabyData + 4 + 24 * i + 16, 8);
223 : }
224 : }
225 12558 : else if (flags & OGR_G_3D)
226 : {
227 72034 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
228 : {
229 69569 : memcpy(&(paoPoints[i].x), pabyData + 4 + 24 * i, 8);
230 69569 : memcpy(&(paoPoints[i].y), pabyData + 4 + 24 * i + 8, 8);
231 69569 : memcpy(padfZ + i, pabyData + 4 + 24 * i + 16, 8);
232 : }
233 : }
234 10093 : else if (nPointCount != 0)
235 : {
236 10091 : memcpy(paoPoints, pabyData + 4, 16 * static_cast<size_t>(nPointCount));
237 : }
238 :
239 : /* -------------------------------------------------------------------- */
240 : /* Byte swap if needed. */
241 : /* -------------------------------------------------------------------- */
242 41957 : if (OGR_SWAP(eByteOrder))
243 : {
244 804 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
245 : {
246 666 : CPL_SWAPDOUBLE(&(paoPoints[i].x));
247 666 : CPL_SWAPDOUBLE(&(paoPoints[i].y));
248 :
249 666 : if (flags & OGR_G_3D)
250 : {
251 85 : CPL_SWAPDOUBLE(padfZ + i);
252 : }
253 666 : if (flags & OGR_G_MEASURED)
254 : {
255 4 : CPL_SWAPDOUBLE(padfM + i);
256 : }
257 : }
258 : }
259 :
260 41957 : return OGRERR_NONE;
261 : }
262 :
263 : /************************************************************************/
264 : /* _exportToWkb() */
265 : /* */
266 : /* Helper method for OGRPolygon. THIS IS NOT THE NORMAL */
267 : /* exportToWkb() METHOD. */
268 : /************************************************************************/
269 :
270 266144 : OGRErr OGRLinearRing::_exportToWkb(int _flags, unsigned char *pabyData,
271 : const OGRwkbExportOptions *psOptions) const
272 :
273 : {
274 :
275 : /* -------------------------------------------------------------------- */
276 : /* Copy in the raw data. */
277 : /* -------------------------------------------------------------------- */
278 266144 : memcpy(pabyData, &nPointCount, 4);
279 :
280 : /* -------------------------------------------------------------------- */
281 : /* Copy in the raw data. */
282 : /* -------------------------------------------------------------------- */
283 266144 : size_t nWords = 0;
284 266144 : if ((_flags & OGR_G_3D) && (_flags & OGR_G_MEASURED))
285 : {
286 13255 : nWords = 4 * static_cast<size_t>(nPointCount);
287 66816 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
288 : {
289 53561 : memcpy(pabyData + 4 + i * 32, &(paoPoints[i].x), 8);
290 53561 : memcpy(pabyData + 4 + i * 32 + 8, &(paoPoints[i].y), 8);
291 53561 : if (padfZ == nullptr)
292 0 : memset(pabyData + 4 + i * 32 + 16, 0, 8);
293 : else
294 53561 : memcpy(pabyData + 4 + i * 32 + 16, padfZ + i, 8);
295 53561 : if (padfM == nullptr)
296 0 : memset(pabyData + 4 + i * 32 + 24, 0, 8);
297 : else
298 53561 : memcpy(pabyData + 4 + i * 32 + 24, padfM + i, 8);
299 : }
300 13255 : OGRRoundCoordinatesIEEE754XYValues<32>(
301 13255 : psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
302 13255 : OGRRoundCoordinatesIEEE754<32>(psOptions->sPrecision.nZBitPrecision,
303 : pabyData + 4 + 2 * sizeof(uint64_t),
304 13255 : nPointCount);
305 13255 : OGRRoundCoordinatesIEEE754<32>(psOptions->sPrecision.nMBitPrecision,
306 : pabyData + 4 + 3 * sizeof(uint64_t),
307 13255 : nPointCount);
308 : }
309 252889 : else if (_flags & OGR_G_MEASURED)
310 : {
311 85 : nWords = 3 * static_cast<size_t>(nPointCount);
312 450 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
313 : {
314 365 : memcpy(pabyData + 4 + i * 24, &(paoPoints[i].x), 8);
315 365 : memcpy(pabyData + 4 + i * 24 + 8, &(paoPoints[i].y), 8);
316 365 : if (padfM == nullptr)
317 0 : memset(pabyData + 4 + i * 24 + 16, 0, 8);
318 : else
319 365 : memcpy(pabyData + 4 + i * 24 + 16, padfM + i, 8);
320 : }
321 85 : OGRRoundCoordinatesIEEE754XYValues<24>(
322 85 : psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
323 85 : OGRRoundCoordinatesIEEE754<24>(psOptions->sPrecision.nMBitPrecision,
324 : pabyData + 4 + 2 * sizeof(uint64_t),
325 85 : nPointCount);
326 : }
327 252804 : else if (_flags & OGR_G_3D)
328 : {
329 160682 : nWords = 3 * static_cast<size_t>(nPointCount);
330 836056 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
331 : {
332 675374 : memcpy(pabyData + 4 + i * 24, &(paoPoints[i].x), 8);
333 675374 : memcpy(pabyData + 4 + i * 24 + 8, &(paoPoints[i].y), 8);
334 675374 : if (padfZ == nullptr)
335 0 : memset(pabyData + 4 + i * 24 + 16, 0, 8);
336 : else
337 675374 : memcpy(pabyData + 4 + i * 24 + 16, padfZ + i, 8);
338 : }
339 160682 : OGRRoundCoordinatesIEEE754XYValues<24>(
340 160682 : psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
341 160682 : OGRRoundCoordinatesIEEE754<24>(psOptions->sPrecision.nZBitPrecision,
342 : pabyData + 4 + 2 * sizeof(uint64_t),
343 160682 : nPointCount);
344 : }
345 : else
346 : {
347 92122 : nWords = 2 * static_cast<size_t>(nPointCount);
348 92122 : memcpy(pabyData + 4, paoPoints, 16 * static_cast<size_t>(nPointCount));
349 92122 : OGRRoundCoordinatesIEEE754XYValues<16>(
350 92122 : psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
351 : }
352 :
353 : /* -------------------------------------------------------------------- */
354 : /* Swap if needed. */
355 : /* -------------------------------------------------------------------- */
356 266141 : if (OGR_SWAP(psOptions->eByteOrder))
357 : {
358 22 : const int nCount = CPL_SWAP32(nPointCount);
359 22 : memcpy(pabyData, &nCount, 4);
360 :
361 257 : for (size_t i = 0; i < nWords; i++)
362 : {
363 235 : CPL_SWAPDOUBLE(pabyData + 4 + 8 * i);
364 : }
365 : }
366 :
367 266141 : return OGRERR_NONE;
368 : }
369 :
370 : /************************************************************************/
371 : /* _WkbSize() */
372 : /* */
373 : /* Helper method for OGRPolygon. NOT THE NORMAL WkbSize() METHOD. */
374 : /************************************************************************/
375 :
376 1244050 : size_t OGRLinearRing::_WkbSize(int _flags) const
377 :
378 : {
379 1244050 : if ((_flags & OGR_G_3D) && (_flags & OGR_G_MEASURED))
380 38857 : return 4 + 32 * static_cast<size_t>(nPointCount);
381 1205190 : else if ((_flags & OGR_G_3D) || (_flags & OGR_G_MEASURED))
382 484546 : return 4 + 24 * static_cast<size_t>(nPointCount);
383 : else
384 720644 : return 4 + 16 * static_cast<size_t>(nPointCount);
385 : }
386 :
387 : //! @endcond
388 :
389 : /************************************************************************/
390 : /* clone() */
391 : /* */
392 : /* We override the OGRCurve clone() to ensure that we get the */
393 : /* correct virtual table. */
394 : /************************************************************************/
395 :
396 1588060 : OGRLinearRing *OGRLinearRing::clone() const
397 :
398 : {
399 1588060 : OGRLinearRing *poNewLinearRing = new OGRLinearRing();
400 1588060 : poNewLinearRing->assignSpatialReference(getSpatialReference());
401 :
402 1588060 : poNewLinearRing->setPoints(nPointCount, paoPoints, padfZ, padfM);
403 1588060 : poNewLinearRing->flags = flags;
404 :
405 1588060 : return poNewLinearRing;
406 : }
407 :
408 : /************************************************************************/
409 : /* reverseWindingOrder() */
410 : /************************************************************************/
411 :
412 : //! @cond Doxygen_Suppress
413 : /** Reverse order of points.
414 : */
415 0 : void OGRLinearRing::reverseWindingOrder()
416 :
417 : {
418 0 : reversePoints();
419 0 : }
420 :
421 : //! @endcond
422 :
423 : /************************************************************************/
424 : /* closeRing() */
425 : /************************************************************************/
426 :
427 106052 : void OGRLinearRing::closeRings()
428 :
429 : {
430 106052 : if (nPointCount < 2)
431 7 : return;
432 :
433 187661 : if (getX(0) != getX(nPointCount - 1) || getY(0) != getY(nPointCount - 1) ||
434 81617 : getZ(0) != getZ(nPointCount - 1))
435 : {
436 48857 : OGRPoint oFirstPoint;
437 24430 : getPoint(0, &oFirstPoint);
438 24429 : addPoint(&oFirstPoint);
439 : }
440 : }
441 :
442 : /************************************************************************/
443 : /* isPointInRing() */
444 : /************************************************************************/
445 :
446 : /** Returns whether the point is inside the ring.
447 : * @param poPoint point
448 : * @param bTestEnvelope set to TRUE if the presence of the point inside the
449 : * ring envelope must be checked first.
450 : * @return TRUE or FALSE.
451 : */
452 2630930 : OGRBoolean OGRLinearRing::isPointInRing(const OGRPoint *poPoint,
453 : int bTestEnvelope) const
454 : {
455 2630930 : if (nullptr == poPoint)
456 : {
457 0 : CPLDebug("OGR",
458 : "OGRLinearRing::isPointInRing(const OGRPoint* poPoint) - "
459 : "passed point is NULL!");
460 0 : return FALSE;
461 : }
462 2630930 : if (poPoint->IsEmpty())
463 : {
464 1 : return FALSE;
465 : }
466 :
467 2630920 : const int iNumPoints = getNumPoints();
468 :
469 : // Simple validation
470 2630920 : if (iNumPoints < 4)
471 0 : return FALSE;
472 :
473 2630920 : const double dfTestX = poPoint->getX();
474 2630920 : const double dfTestY = poPoint->getY();
475 :
476 : // Fast test if point is inside extent of the ring.
477 2630920 : if (bTestEnvelope)
478 : {
479 2630310 : OGREnvelope extent;
480 2630310 : getEnvelope(&extent);
481 2630310 : if (!(dfTestX >= extent.MinX && dfTestX <= extent.MaxX &&
482 1132360 : dfTestY >= extent.MinY && dfTestY <= extent.MaxY))
483 : {
484 2346230 : return FALSE;
485 : }
486 : }
487 :
488 : // For every point p in ring,
489 : // test if ray starting from given point crosses segment (p - 1, p)
490 284693 : int iNumCrossings = 0;
491 :
492 284693 : double prev_diff_x = getX(0) - dfTestX;
493 284693 : double prev_diff_y = getY(0) - dfTestY;
494 :
495 1508470 : for (int iPoint = 1; iPoint < iNumPoints; iPoint++)
496 : {
497 1223780 : const double x1 = getX(iPoint) - dfTestX;
498 1223780 : const double y1 = getY(iPoint) - dfTestY;
499 :
500 1223780 : const double x2 = prev_diff_x;
501 1223780 : const double y2 = prev_diff_y;
502 :
503 1223780 : if (((y1 > 0) && (y2 <= 0)) || ((y2 > 0) && (y1 <= 0)))
504 : {
505 : // Check if ray intersects with segment of the ring
506 563956 : const double dfIntersection = (x1 * y2 - x2 * y1) / (y2 - y1);
507 563956 : if (0.0 < dfIntersection)
508 : {
509 : // Count intersections
510 276703 : iNumCrossings++;
511 : }
512 : }
513 :
514 1223780 : prev_diff_x = x1;
515 1223780 : prev_diff_y = y1;
516 : }
517 :
518 : // If iNumCrossings number is even, given point is outside the ring,
519 : // when the crossings number is odd, the point is inside the ring.
520 284693 : return iNumCrossings % 2; // OGRBoolean
521 : }
522 :
523 : /************************************************************************/
524 : /* isPointOnRingBoundary() */
525 : /************************************************************************/
526 :
527 : /** Returns whether the point is on the ring boundary.
528 : * @param poPoint point
529 : * @param bTestEnvelope set to TRUE if the presence of the point inside the
530 : * ring envelope must be checked first.
531 : * @return TRUE or FALSE.
532 : */
533 2389160 : OGRBoolean OGRLinearRing::isPointOnRingBoundary(const OGRPoint *poPoint,
534 : int bTestEnvelope) const
535 : {
536 2389160 : if (nullptr == poPoint)
537 : {
538 0 : CPLDebug("OGR", "OGRLinearRing::isPointOnRingBoundary(const OGRPoint* "
539 : "poPoint) - passed point is NULL!");
540 0 : return 0;
541 : }
542 :
543 2389160 : const int iNumPoints = getNumPoints();
544 :
545 : // Simple validation.
546 2389160 : if (iNumPoints < 4)
547 0 : return 0;
548 :
549 2389160 : const double dfTestX = poPoint->getX();
550 2389160 : const double dfTestY = poPoint->getY();
551 :
552 : // Fast test if point is inside extent of the ring
553 2389160 : if (bTestEnvelope)
554 : {
555 2388510 : OGREnvelope extent;
556 2388510 : getEnvelope(&extent);
557 2388510 : if (!(dfTestX >= extent.MinX && dfTestX <= extent.MaxX &&
558 890556 : dfTestY >= extent.MinY && dfTestY <= extent.MaxY))
559 : {
560 2346230 : return 0;
561 : }
562 : }
563 :
564 42937 : double prev_diff_x = dfTestX - getX(0);
565 42937 : double prev_diff_y = dfTestY - getY(0);
566 :
567 246272 : for (int iPoint = 1; iPoint < iNumPoints; iPoint++)
568 : {
569 205018 : const double dx1 = dfTestX - getX(iPoint);
570 205018 : const double dy1 = dfTestY - getY(iPoint);
571 :
572 205018 : const double dx2 = prev_diff_x;
573 205018 : const double dy2 = prev_diff_y;
574 :
575 : // If the point is on the segment, return immediately.
576 : // FIXME? If the test point is not exactly identical to one of
577 : // the vertices of the ring, but somewhere on a segment, there's
578 : // little chance that we get 0. So that should be tested against some
579 : // epsilon.
580 :
581 205018 : if (dx1 * dy2 - dx2 * dy1 == 0)
582 : {
583 : // If iPoint and iPointPrev are the same, go on.
584 2044 : if (!(dx1 == dx2 && dy1 == dy2))
585 : {
586 1760 : const double dx_segment = getX(iPoint) - getX(iPoint - 1);
587 1760 : const double dy_segment = getY(iPoint) - getY(iPoint - 1);
588 1760 : const double crossproduct = dx2 * dx_segment + dy2 * dy_segment;
589 1760 : if (crossproduct >= 0)
590 : {
591 1758 : const double sq_length_seg =
592 1758 : dx_segment * dx_segment + dy_segment * dy_segment;
593 1758 : if (crossproduct <= sq_length_seg)
594 : {
595 1683 : return 1;
596 : }
597 : }
598 : }
599 : }
600 :
601 203335 : prev_diff_x = dx1;
602 203335 : prev_diff_y = dy1;
603 : }
604 :
605 41254 : return 0;
606 : }
607 :
608 : /************************************************************************/
609 : /* transform() */
610 : /************************************************************************/
611 :
612 730 : OGRErr OGRLinearRing::transform(OGRCoordinateTransformation *poCT)
613 :
614 : {
615 730 : const bool bIsClosed = getNumPoints() > 2 && CPL_TO_BOOL(get_IsClosed());
616 730 : OGRErr eErr = OGRLineString::transform(poCT);
617 730 : if (bIsClosed && eErr == OGRERR_NONE && !get_IsClosed())
618 : {
619 0 : CPLDebug("OGR", "Linearring is not closed after coordinate "
620 : "transformation. Forcing last point to be identical to "
621 : "first one");
622 : // Force last point to be identical to first point.
623 : // This is a safety belt in case the reprojection of the same coordinate
624 : // isn't perfectly stable. This can for example happen in very rare
625 : // cases when reprojecting a cutline with a RPC transform with a DEM
626 : // that is a VRT whose sources are resampled...
627 0 : OGRPoint oStartPoint;
628 0 : StartPoint(&oStartPoint);
629 :
630 0 : setPoint(getNumPoints() - 1, &oStartPoint);
631 : }
632 730 : return eErr;
633 : }
634 :
635 : /************************************************************************/
636 : /* CastToLineString() */
637 : /************************************************************************/
638 :
639 : /**
640 : * \brief Cast to line string.
641 : *
642 : * The passed in geometry is consumed and a new one returned .
643 : *
644 : * @param poLR the input geometry - ownership is passed to the method.
645 : * @return new geometry.
646 : */
647 :
648 67 : OGRLineString *OGRLinearRing::CastToLineString(OGRLinearRing *poLR)
649 : {
650 67 : return TransferMembersAndDestroy(poLR, new OGRLineString());
651 : }
652 :
653 : //! @cond Doxygen_Suppress
654 : /************************************************************************/
655 : /* GetCasterToLineString() */
656 : /************************************************************************/
657 :
658 40 : OGRLineString *OGRLinearRing::CasterToLineString(OGRCurve *poCurve)
659 : {
660 40 : return OGRLinearRing::CastToLineString(poCurve->toLinearRing());
661 : }
662 :
663 40 : OGRCurveCasterToLineString OGRLinearRing::GetCasterToLineString() const
664 : {
665 40 : return OGRLinearRing::CasterToLineString;
666 : }
667 :
668 : /************************************************************************/
669 : /* GetCasterToLinearRing() */
670 : /************************************************************************/
671 :
672 4 : static OGRLinearRing *CasterToLinearRing(OGRCurve *poCurve)
673 : {
674 4 : return poCurve->toLinearRing();
675 : }
676 :
677 4 : OGRCurveCasterToLinearRing OGRLinearRing::GetCasterToLinearRing() const
678 : {
679 4 : return ::CasterToLinearRing;
680 : }
681 :
682 : //! @endcond
|