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