Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements decoder of shapebin geometry for PGeo
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : * Paul Ramsey, pramsey at cleverelephant.ca
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
10 : * Copyright (c) 2011, Paul Ramsey <pramsey at cleverelephant.ca>
11 : * Copyright (c) 2011-2014, Even Rouault <even dot rouault at spatialys.com>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : // PGeo == ESRI Personal GeoDatabase.
17 :
18 : #include "cpl_port.h"
19 : #include "ogrpgeogeometry.h"
20 :
21 : #include <algorithm>
22 : #include <cassert>
23 : #include <cmath>
24 : #include <cstddef>
25 : #include <cstring>
26 : #include <limits>
27 : #include <map>
28 : #include <memory>
29 : #include <set>
30 : #include <utility>
31 : #include <vector>
32 :
33 : #include "cpl_conv.h"
34 : #include "cpl_error.h"
35 : #include "cpl_string.h"
36 : #include "cpl_vsi.h"
37 : #include "ogr_api.h"
38 : #include "ogr_core.h"
39 : #include "ogr_p.h"
40 :
41 : #ifdef HAVE_WFLAG_UNREACHABLE_CODE_AGGRESSIVE
42 : #if defined(__clang__)
43 : #pragma clang diagnostic push
44 : #pragma clang diagnostic ignored "-Wunreachable-code"
45 : #endif
46 : #endif
47 :
48 : constexpr int SHPP_TRISTRIP = 0;
49 : constexpr int SHPP_TRIFAN = 1;
50 : constexpr int SHPP_OUTERRING = 2;
51 : constexpr int SHPP_INNERRING = 3;
52 : constexpr int SHPP_FIRSTRING = 4;
53 : constexpr int SHPP_RING = 5;
54 : constexpr int SHPP_TRIANGLES = 6; // Multipatch 9.0 specific.
55 :
56 : enum CurveType
57 : {
58 : CURVE_ARC_INTERIOR_POINT,
59 : CURVE_ARC_CENTER_POINT,
60 : CURVE_BEZIER,
61 : CURVE_ELLIPSE_BY_CENTER
62 : };
63 :
64 : namespace
65 : {
66 : struct CurveSegment
67 : {
68 : int nStartPointIdx;
69 : CurveType eType;
70 :
71 : union
72 : {
73 : // Arc defined by an intermediate point.
74 : struct
75 : {
76 : double dfX;
77 : double dfY;
78 : } ArcByIntermediatePoint;
79 :
80 : // Deprecated way of defining circular arc by its center and
81 : // winding order.
82 : struct
83 : {
84 : double dfX;
85 : double dfY;
86 : bool bIsCCW;
87 : } ArcByCenterPoint;
88 :
89 : struct
90 : {
91 : double dfX1;
92 : double dfY1;
93 : double dfX2;
94 : double dfY2;
95 : } Bezier;
96 :
97 : struct
98 : {
99 : double dfX;
100 : double dfY;
101 : double dfRotationDeg;
102 : double dfSemiMajor;
103 : double dfRatioSemiMinor;
104 : bool bIsMinor;
105 : bool bIsComplete;
106 : } EllipseByCenter;
107 : } u;
108 : };
109 : } /* namespace */
110 :
111 : constexpr int EXT_SHAPE_SEGMENT_ARC = 1;
112 : constexpr int EXT_SHAPE_SEGMENT_BEZIER = 4;
113 : constexpr int EXT_SHAPE_SEGMENT_ELLIPSE = 5;
114 :
115 : constexpr int EXT_SHAPE_ARC_EMPTY = 0x1;
116 : constexpr int EXT_SHAPE_ARC_CCW = 0x8;
117 : #ifdef DEBUG_VERBOSE
118 : constexpr int EXT_SHAPE_ARC_MINOR = 0x10;
119 : #endif
120 : constexpr int EXT_SHAPE_ARC_LINE = 0x20;
121 : constexpr int EXT_SHAPE_ARC_POINT = 0x40;
122 : constexpr int EXT_SHAPE_ARC_IP = 0x80;
123 :
124 : #ifdef DEBUG_VERBOSE
125 : constexpr int EXT_SHAPE_ELLIPSE_EMPTY = 0x1;
126 : constexpr int EXT_SHAPE_ELLIPSE_LINE = 0x40;
127 : constexpr int EXT_SHAPE_ELLIPSE_POINT = 0x80;
128 : constexpr int EXT_SHAPE_ELLIPSE_CIRCULAR = 0x100;
129 : constexpr int EXT_SHAPE_ELLIPSE_CCW = 0x800;
130 : #endif
131 :
132 : constexpr int EXT_SHAPE_ELLIPSE_CENTER_TO = 0x200;
133 : constexpr int EXT_SHAPE_ELLIPSE_CENTER_FROM = 0x400;
134 : constexpr int EXT_SHAPE_ELLIPSE_MINOR = 0x1000;
135 : constexpr int EXT_SHAPE_ELLIPSE_COMPLETE = 0x2000;
136 :
137 : /************************************************************************/
138 : /* OGRCreateFromMultiPatchPart() */
139 : /************************************************************************/
140 :
141 3536 : static void OGRCreateFromMultiPatchPart(OGRGeometryCollection *poGC,
142 : OGRMultiPolygon *&poMP,
143 : OGRPolygon *&poLastPoly, int nPartType,
144 : int nPartPoints, const double *padfX,
145 : const double *padfY,
146 : const double *padfZ)
147 : {
148 3536 : nPartType &= 0xf;
149 :
150 3536 : if (nPartType == SHPP_TRISTRIP)
151 : {
152 800 : if (poMP != nullptr && poLastPoly != nullptr)
153 : {
154 0 : poMP->addGeometryDirectly(poLastPoly);
155 0 : poLastPoly = nullptr;
156 : }
157 :
158 800 : OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
159 2404 : for (int iBaseVert = 0; iBaseVert < nPartPoints - 2; iBaseVert++)
160 : {
161 1604 : const int iSrcVert = iBaseVert;
162 :
163 3208 : OGRPoint oPoint1(padfX[iSrcVert], padfY[iSrcVert], padfZ[iSrcVert]);
164 :
165 1604 : OGRPoint oPoint2(padfX[iSrcVert + 1], padfY[iSrcVert + 1],
166 3208 : padfZ[iSrcVert + 1]);
167 :
168 1604 : OGRPoint oPoint3(padfX[iSrcVert + 2], padfY[iSrcVert + 2],
169 3208 : padfZ[iSrcVert + 2]);
170 :
171 : OGRTriangle *poTriangle =
172 1604 : new OGRTriangle(oPoint1, oPoint2, oPoint3);
173 :
174 1604 : poTIN->addGeometryDirectly(poTriangle);
175 : }
176 800 : poGC->addGeometryDirectly(poTIN);
177 : }
178 2736 : else if (nPartType == SHPP_TRIFAN)
179 : {
180 691 : if (poMP != nullptr && poLastPoly != nullptr)
181 : {
182 0 : poMP->addGeometryDirectly(poLastPoly);
183 0 : poLastPoly = nullptr;
184 : }
185 :
186 691 : OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
187 2068 : for (int iBaseVert = 0; iBaseVert < nPartPoints - 2; iBaseVert++)
188 : {
189 1377 : const int iSrcVert = iBaseVert;
190 :
191 2754 : OGRPoint oPoint1(padfX[0], padfY[0], padfZ[0]);
192 :
193 1377 : OGRPoint oPoint2(padfX[iSrcVert + 1], padfY[iSrcVert + 1],
194 2754 : padfZ[iSrcVert + 1]);
195 :
196 1377 : OGRPoint oPoint3(padfX[iSrcVert + 2], padfY[iSrcVert + 2],
197 2754 : padfZ[iSrcVert + 2]);
198 :
199 : OGRTriangle *poTriangle =
200 1377 : new OGRTriangle(oPoint1, oPoint2, oPoint3);
201 :
202 1377 : poTIN->addGeometryDirectly(poTriangle);
203 : }
204 691 : poGC->addGeometryDirectly(poTIN);
205 : }
206 2045 : else if (nPartType == SHPP_OUTERRING || nPartType == SHPP_INNERRING ||
207 682 : nPartType == SHPP_FIRSTRING || nPartType == SHPP_RING)
208 : {
209 1363 : if (poMP == nullptr)
210 : {
211 681 : poMP = new OGRMultiPolygon();
212 : }
213 :
214 1363 : if (poMP != nullptr && poLastPoly != nullptr &&
215 682 : (nPartType == SHPP_OUTERRING || nPartType == SHPP_FIRSTRING))
216 : {
217 0 : poMP->addGeometryDirectly(poLastPoly);
218 0 : poLastPoly = nullptr;
219 : }
220 :
221 1363 : if (poLastPoly == nullptr)
222 681 : poLastPoly = new OGRPolygon();
223 :
224 1363 : OGRLinearRing *poRing = new OGRLinearRing;
225 :
226 1363 : poRing->setPoints(nPartPoints, const_cast<double *>(padfX),
227 : const_cast<double *>(padfY),
228 : const_cast<double *>(padfZ));
229 :
230 1363 : poRing->closeRings();
231 :
232 1363 : poLastPoly->addRingDirectly(poRing);
233 : }
234 682 : else if (nPartType == SHPP_TRIANGLES)
235 : {
236 682 : if (poMP != nullptr && poLastPoly != nullptr)
237 : {
238 0 : poMP->addGeometryDirectly(poLastPoly);
239 0 : poLastPoly = nullptr;
240 : }
241 :
242 682 : OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
243 1364 : for (int iBaseVert = 0; iBaseVert < nPartPoints - 2; iBaseVert += 3)
244 : {
245 682 : const int iSrcVert = iBaseVert;
246 :
247 1364 : OGRPoint oPoint1(padfX[iSrcVert], padfY[iSrcVert], padfZ[iSrcVert]);
248 :
249 682 : OGRPoint oPoint2(padfX[iSrcVert + 1], padfY[iSrcVert + 1],
250 1364 : padfZ[iSrcVert + 1]);
251 :
252 682 : OGRPoint oPoint3(padfX[iSrcVert + 2], padfY[iSrcVert + 2],
253 1364 : padfZ[iSrcVert + 2]);
254 :
255 : OGRTriangle *poTriangle =
256 682 : new OGRTriangle(oPoint1, oPoint2, oPoint3);
257 :
258 682 : poTIN->addGeometryDirectly(poTriangle);
259 : }
260 682 : poGC->addGeometryDirectly(poTIN);
261 : }
262 : else
263 0 : CPLDebug("OGR", "Unrecognized parttype %d, ignored.", nPartType);
264 3536 : }
265 :
266 : static bool
267 12 : RegisterEdge(const double *padfX, const double *padfY, const double *padfZ,
268 : int nPart,
269 : std::map<std::vector<double>, std::pair<int, int>> &oMapEdges)
270 : {
271 12 : int idx = 0;
272 12 : if (padfX[0] > padfX[1])
273 : {
274 6 : idx = 1;
275 : }
276 6 : else if (padfX[0] == padfX[1])
277 : {
278 2 : if (padfY[0] > padfY[1])
279 : {
280 0 : idx = 1;
281 : }
282 2 : else if (padfY[0] == padfY[1])
283 : {
284 0 : if (padfZ[0] > padfZ[1])
285 : {
286 0 : idx = 1;
287 : }
288 : }
289 : }
290 24 : std::vector<double> oVector;
291 12 : oVector.push_back(padfX[idx]);
292 12 : oVector.push_back(padfY[idx]);
293 12 : oVector.push_back(padfZ[idx]);
294 12 : oVector.push_back(padfX[1 - idx]);
295 12 : oVector.push_back(padfY[1 - idx]);
296 12 : oVector.push_back(padfZ[1 - idx]);
297 12 : const auto oIter = oMapEdges.find(oVector);
298 12 : if (oIter == oMapEdges.end())
299 : {
300 10 : oMapEdges[oVector] = std::pair(nPart, -1);
301 : }
302 : else
303 : {
304 2 : CPLAssert(oIter->second.first >= 0);
305 2 : if (oIter->second.second < 0)
306 2 : oIter->second.second = nPart;
307 : else
308 0 : return false;
309 : }
310 12 : return true;
311 : }
312 :
313 12 : static const std::pair<int, int> &GetEdgeOwners(
314 : const double *padfX, const double *padfY, const double *padfZ,
315 : const std::map<std::vector<double>, std::pair<int, int>> &oMapEdges)
316 : {
317 12 : int idx = 0;
318 12 : if (padfX[0] > padfX[1])
319 : {
320 6 : idx = 1;
321 : }
322 6 : else if (padfX[0] == padfX[1])
323 : {
324 2 : if (padfY[0] > padfY[1])
325 : {
326 0 : idx = 1;
327 : }
328 2 : else if (padfY[0] == padfY[1])
329 : {
330 0 : if (padfZ[0] > padfZ[1])
331 : {
332 0 : idx = 1;
333 : }
334 : }
335 : }
336 12 : std::vector<double> oVector;
337 12 : oVector.push_back(padfX[idx]);
338 12 : oVector.push_back(padfY[idx]);
339 12 : oVector.push_back(padfZ[idx]);
340 12 : oVector.push_back(padfX[1 - idx]);
341 12 : oVector.push_back(padfY[1 - idx]);
342 12 : oVector.push_back(padfZ[1 - idx]);
343 12 : const auto oIter = oMapEdges.find(oVector);
344 12 : CPLAssert(oIter != oMapEdges.end());
345 24 : return oIter->second;
346 : }
347 :
348 : /************************************************************************/
349 : /* OGRCreateFromMultiPatch() */
350 : /* */
351 : /* Translate a multipatch representation to an OGR geometry */
352 : /************************************************************************/
353 :
354 812 : OGRGeometry *OGRCreateFromMultiPatch(int nParts, const GInt32 *panPartStart,
355 : const GInt32 *panPartType, int nPoints,
356 : const double *padfX, const double *padfY,
357 : const double *padfZ)
358 : {
359 : // Deal with particular case of a patch of OuterRing of 4 points
360 : // that form a TIN. And be robust to consecutive duplicated triangles !
361 1624 : std::map<std::vector<double>, std::pair<int, int>> oMapEdges;
362 812 : bool bTINCandidate = nParts >= 2;
363 1624 : std::set<int> oSetDuplicated;
364 817 : for (int iPart = 0; iPart < nParts && panPartStart != nullptr; iPart++)
365 : {
366 815 : int nPartPoints = 0;
367 :
368 : // Figure out details about this part's vertex list.
369 815 : if (iPart == nParts - 1)
370 129 : nPartPoints = nPoints - panPartStart[iPart];
371 : else
372 686 : nPartPoints = panPartStart[iPart + 1] - panPartStart[iPart];
373 815 : const int nPartStart = panPartStart[iPart];
374 :
375 5 : if (panPartType[iPart] == SHPP_OUTERRING && nPartPoints == 4 &&
376 5 : padfX[nPartStart] == padfX[nPartStart + 3] &&
377 5 : padfY[nPartStart] == padfY[nPartStart + 3] &&
378 5 : padfZ[nPartStart] == padfZ[nPartStart + 3] &&
379 5 : !std::isnan(padfX[nPartStart]) &&
380 5 : !std::isnan(padfX[nPartStart + 1]) &&
381 5 : !std::isnan(padfX[nPartStart + 2]) &&
382 5 : !std::isnan(padfY[nPartStart]) &&
383 5 : !std::isnan(padfY[nPartStart + 1]) &&
384 5 : !std::isnan(padfY[nPartStart + 2]) &&
385 5 : !std::isnan(padfZ[nPartStart]) &&
386 825 : !std::isnan(padfZ[nPartStart + 1]) &&
387 5 : !std::isnan(padfZ[nPartStart + 2]))
388 : {
389 5 : bool bDuplicate = false;
390 5 : if (iPart > 0)
391 : {
392 3 : bDuplicate = true;
393 3 : const int nPrevPartStart = panPartStart[iPart - 1];
394 6 : for (int j = 0; j < 3; j++)
395 : {
396 5 : if (padfX[nPartStart + j] == padfX[nPrevPartStart + j] &&
397 3 : padfY[nPartStart + j] == padfY[nPrevPartStart + j] &&
398 3 : padfZ[nPartStart + j] == padfZ[nPrevPartStart + j])
399 : {
400 : }
401 : else
402 : {
403 2 : bDuplicate = false;
404 2 : break;
405 : }
406 : }
407 : }
408 5 : if (bDuplicate)
409 : {
410 1 : oSetDuplicated.insert(iPart);
411 : }
412 12 : else if (RegisterEdge(padfX + nPartStart, padfY + nPartStart,
413 4 : padfZ + nPartStart, iPart, oMapEdges) &&
414 4 : RegisterEdge(padfX + nPartStart + 1,
415 4 : padfY + nPartStart + 1,
416 8 : padfZ + nPartStart + 1, iPart, oMapEdges) &&
417 4 : RegisterEdge(padfX + nPartStart + 2,
418 4 : padfY + nPartStart + 2,
419 4 : padfZ + nPartStart + 2, iPart, oMapEdges))
420 : {
421 : // ok
422 : }
423 : else
424 : {
425 0 : bTINCandidate = false;
426 0 : break;
427 : }
428 : }
429 : else
430 : {
431 810 : bTINCandidate = false;
432 810 : break;
433 : }
434 : }
435 812 : if (bTINCandidate && panPartStart != nullptr)
436 : {
437 2 : std::set<int> oVisitedParts;
438 2 : std::set<int> oToBeVisitedParts;
439 2 : oToBeVisitedParts.insert(0);
440 6 : while (!oToBeVisitedParts.empty())
441 : {
442 4 : const int iPart = *(oToBeVisitedParts.begin());
443 4 : oVisitedParts.insert(iPart);
444 4 : oToBeVisitedParts.erase(iPart);
445 :
446 4 : const int nPartStart = panPartStart[iPart];
447 16 : for (int j = 0; j < 3; j++)
448 : {
449 : const auto &oPair = GetEdgeOwners(
450 12 : padfX + nPartStart + j, padfY + nPartStart + j,
451 12 : padfZ + nPartStart + j, oMapEdges);
452 12 : const int iOtherPart =
453 12 : (oPair.first == iPart) ? oPair.second : oPair.first;
454 16 : if (iOtherPart >= 0 &&
455 16 : oVisitedParts.find(iOtherPart) == oVisitedParts.end())
456 : {
457 2 : oToBeVisitedParts.insert(iOtherPart);
458 : }
459 : }
460 : }
461 2 : if (static_cast<int>(oVisitedParts.size()) ==
462 2 : nParts - static_cast<int>(oSetDuplicated.size()))
463 : {
464 2 : OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
465 7 : for (int iPart = 0; iPart < nParts; iPart++)
466 : {
467 5 : if (oSetDuplicated.find(iPart) != oSetDuplicated.end())
468 1 : continue;
469 :
470 4 : const int nPartStart = panPartStart[iPart];
471 4 : OGRPoint oPoint1(padfX[nPartStart], padfY[nPartStart],
472 8 : padfZ[nPartStart]);
473 :
474 4 : OGRPoint oPoint2(padfX[nPartStart + 1], padfY[nPartStart + 1],
475 8 : padfZ[nPartStart + 1]);
476 :
477 4 : OGRPoint oPoint3(padfX[nPartStart + 2], padfY[nPartStart + 2],
478 8 : padfZ[nPartStart + 2]);
479 :
480 : OGRTriangle *poTriangle =
481 4 : new OGRTriangle(oPoint1, oPoint2, oPoint3);
482 :
483 4 : poTIN->addGeometryDirectly(poTriangle);
484 : }
485 2 : return poTIN;
486 : }
487 : }
488 :
489 810 : OGRGeometryCollection *poGC = new OGRGeometryCollection();
490 810 : OGRMultiPolygon *poMP = nullptr;
491 810 : OGRPolygon *poLastPoly = nullptr;
492 4346 : for (int iPart = 0; iPart < nParts; iPart++)
493 : {
494 3536 : int nPartPoints = 0;
495 3536 : int nPartStart = 0;
496 :
497 : // Figure out details about this part's vertex list.
498 3536 : if (panPartStart == nullptr)
499 : {
500 0 : nPartPoints = nPoints;
501 : }
502 : else
503 : {
504 :
505 3536 : if (iPart == nParts - 1)
506 810 : nPartPoints = nPoints - panPartStart[iPart];
507 : else
508 2726 : nPartPoints = panPartStart[iPart + 1] - panPartStart[iPart];
509 3536 : nPartStart = panPartStart[iPart];
510 : }
511 :
512 3536 : OGRCreateFromMultiPatchPart(poGC, poMP, poLastPoly, panPartType[iPart],
513 3536 : nPartPoints, padfX + nPartStart,
514 3536 : padfY + nPartStart, padfZ + nPartStart);
515 : }
516 :
517 810 : if (poMP != nullptr && poLastPoly != nullptr)
518 : {
519 681 : poMP->addGeometryDirectly(poLastPoly);
520 : // poLastPoly = NULL;
521 : }
522 810 : if (poMP != nullptr)
523 681 : poGC->addGeometryDirectly(poMP);
524 :
525 810 : if (poGC->getNumGeometries() == 1)
526 : {
527 127 : OGRGeometry *poResultGeom = poGC->getGeometryRef(0);
528 127 : poGC->removeGeometry(0, FALSE);
529 127 : delete poGC;
530 127 : return poResultGeom;
531 : }
532 :
533 : else
534 683 : return poGC;
535 : }
536 :
537 : /************************************************************************/
538 : /* OGRWriteToShapeBin() */
539 : /* */
540 : /* Translate OGR geometry to a shapefile binary representation */
541 : /************************************************************************/
542 :
543 0 : OGRErr OGRWriteToShapeBin(const OGRGeometry *poGeom, GByte **ppabyShape,
544 : int *pnBytes)
545 : {
546 0 : int nShpSize = 4; // All types start with integer type number.
547 :
548 : /* -------------------------------------------------------------------- */
549 : /* Null or Empty input maps to SHPT_NULL. */
550 : /* -------------------------------------------------------------------- */
551 0 : if (!poGeom || poGeom->IsEmpty())
552 : {
553 0 : *ppabyShape = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nShpSize));
554 0 : if (*ppabyShape == nullptr)
555 0 : return OGRERR_FAILURE;
556 0 : GUInt32 zero = SHPT_NULL;
557 0 : memcpy(*ppabyShape, &zero, nShpSize);
558 0 : *pnBytes = nShpSize;
559 0 : return OGRERR_NONE;
560 : }
561 :
562 0 : const OGRwkbGeometryType nOGRType = wkbFlatten(poGeom->getGeometryType());
563 0 : const bool b3d = wkbHasZ(poGeom->getGeometryType());
564 0 : const bool bHasM = wkbHasM(poGeom->getGeometryType());
565 0 : const int nCoordDims = poGeom->CoordinateDimension();
566 :
567 0 : int nShpZSize = 0; // Z gets tacked onto the end.
568 0 : GUInt32 nPoints = 0;
569 0 : GUInt32 nParts = 0;
570 :
571 : /* -------------------------------------------------------------------- */
572 : /* Calculate the shape buffer size */
573 : /* -------------------------------------------------------------------- */
574 0 : if (nOGRType == wkbPoint)
575 : {
576 0 : nShpSize += 8 * nCoordDims;
577 : }
578 0 : else if (nOGRType == wkbLineString)
579 : {
580 0 : const OGRLineString *poLine = poGeom->toLineString();
581 0 : nPoints = poLine->getNumPoints();
582 0 : nParts = 1;
583 0 : nShpSize += 16 * nCoordDims; // xy(z)(m) box.
584 0 : nShpSize += 4; // nparts.
585 0 : nShpSize += 4; // npoints.
586 0 : nShpSize += 4; // Parts[1].
587 0 : nShpSize += 8 * nCoordDims * nPoints; // Points.
588 0 : nShpZSize = 16 + 8 * nPoints;
589 : }
590 0 : else if (nOGRType == wkbPolygon)
591 : {
592 0 : std::unique_ptr<OGRPolygon> poPoly(poGeom->toPolygon()->clone());
593 0 : poPoly->closeRings();
594 0 : nParts = poPoly->getNumInteriorRings() + 1;
595 0 : for (const auto poRing : *poPoly)
596 : {
597 0 : nPoints += poRing->getNumPoints();
598 : }
599 0 : nShpSize += 16 * nCoordDims; // xy(z)(m) box.
600 0 : nShpSize += 4; // nparts.
601 0 : nShpSize += 4; // npoints.
602 0 : nShpSize += 4 * nParts; // parts[nparts]
603 0 : nShpSize += 8 * nCoordDims * nPoints; // Points.
604 0 : nShpZSize = 16 + 8 * nPoints;
605 : }
606 0 : else if (nOGRType == wkbMultiPoint)
607 : {
608 0 : const OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
609 0 : for (const auto poPoint : *poMPoint)
610 : {
611 0 : if (poPoint->IsEmpty())
612 0 : continue;
613 0 : nPoints++;
614 : }
615 0 : nShpSize += 16 * nCoordDims; // xy(z)(m) box.
616 0 : nShpSize += 4; // npoints.
617 0 : nShpSize += 8 * nCoordDims * nPoints; // Points.
618 0 : nShpZSize = 16 + 8 * nPoints;
619 : }
620 0 : else if (nOGRType == wkbMultiLineString)
621 : {
622 0 : const OGRMultiLineString *poMLine = poGeom->toMultiLineString();
623 0 : for (const auto poLine : *poMLine)
624 : {
625 : // Skip empties.
626 0 : if (poLine->IsEmpty())
627 0 : continue;
628 0 : nParts++;
629 0 : nPoints += poLine->getNumPoints();
630 : }
631 0 : nShpSize += 16 * nCoordDims; //* xy(z)(m) box.
632 0 : nShpSize += 4; // nparts.
633 0 : nShpSize += 4; // npoints.
634 0 : nShpSize += 4 * nParts; // parts[nparts].
635 0 : nShpSize += 8 * nCoordDims * nPoints; // Points.
636 0 : nShpZSize = 16 + 8 * nPoints;
637 : }
638 0 : else if (nOGRType == wkbMultiPolygon)
639 : {
640 : std::unique_ptr<OGRMultiPolygon> poMPoly(
641 0 : poGeom->toMultiPolygon()->clone());
642 0 : poMPoly->closeRings();
643 0 : for (const auto poPoly : *poMPoly)
644 : {
645 : // Skip empties.
646 0 : if (poPoly->IsEmpty())
647 0 : continue;
648 :
649 0 : const int nRings = poPoly->getNumInteriorRings() + 1;
650 0 : nParts += nRings;
651 0 : for (const auto poRing : *poPoly)
652 : {
653 0 : nPoints += poRing->getNumPoints();
654 : }
655 : }
656 0 : nShpSize += 16 * nCoordDims; // xy(z)(m) box.
657 0 : nShpSize += 4; // nparts.
658 0 : nShpSize += 4; // npoints.
659 0 : nShpSize += 4 * nParts; // parts[nparts].
660 0 : nShpSize += 8 * nCoordDims * nPoints; // Points.
661 0 : nShpZSize = 16 + 8 * nPoints;
662 : }
663 : else
664 : {
665 0 : return OGRERR_UNSUPPORTED_OPERATION;
666 : }
667 :
668 : // #define WRITE_ARC_HACK
669 : #ifdef WRITE_ARC_HACK
670 : int nShpSizeBeforeCurve = nShpSize;
671 : nShpSize += 4 + 4 + 4 + 20;
672 : #endif
673 : // Allocate our shape buffer.
674 0 : *ppabyShape = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nShpSize));
675 0 : if (!*ppabyShape)
676 0 : return OGRERR_FAILURE;
677 :
678 : #ifdef WRITE_ARC_HACK
679 : /* To be used with:
680 : id,WKT
681 : 1,"LINESTRING (1 0,0 1)"
682 : 2,"LINESTRING (5 1,6 0)"
683 : 3,"LINESTRING (10 1,11 0)"
684 : 4,"LINESTRING (16 0,15 1)"
685 : 5,"LINESTRING (21 0,20 1)"
686 : 6,"LINESTRING (31 0,30 2)" <-- not constant radius
687 : */
688 :
689 : GUInt32 nTmp = 1;
690 : memcpy((*ppabyShape) + nShpSizeBeforeCurve, &nTmp, 4);
691 : nTmp = 0;
692 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 4, &nTmp, 4);
693 : nTmp = EXT_SHAPE_SEGMENT_ARC;
694 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8, &nTmp, 4);
695 : static int nCounter = 0;
696 : nCounter++;
697 : if (nCounter == 1)
698 : {
699 : double dfVal = 0;
700 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
701 : dfVal = 0;
702 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
703 : nTmp = 0;
704 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
705 : }
706 : else if (nCounter == 2)
707 : {
708 : double dfVal = 5;
709 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
710 : dfVal = 0;
711 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
712 : nTmp = EXT_SHAPE_ARC_MINOR;
713 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
714 : }
715 : else if (nCounter == 3)
716 : {
717 : double dfVal = 10;
718 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
719 : dfVal = 0;
720 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
721 : nTmp = EXT_SHAPE_ARC_CCW;
722 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
723 : }
724 : else if (nCounter == 4)
725 : {
726 : double dfVal = 15;
727 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
728 : dfVal = 0;
729 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
730 : nTmp = EXT_SHAPE_ARC_CCW | EXT_SHAPE_ARC_MINOR;
731 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
732 : }
733 : else if (nCounter == 5)
734 : {
735 : double dfVal = 20;
736 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
737 : dfVal = 0;
738 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
739 : // Inconsistent with SP and EP. Only the CCW/not CCW is taken into
740 : // account by ArcGIS.
741 : nTmp = EXT_SHAPE_ARC_MINOR;
742 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
743 : }
744 : else if (nCounter == 6)
745 : {
746 : double dfVal = 30; // Radius inconsistent with SP and EP.
747 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
748 : dfVal = 0;
749 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
750 : nTmp = EXT_SHAPE_ARC_MINOR;
751 : memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
752 : }
753 : #endif
754 :
755 : // Fill in the output size.
756 0 : *pnBytes = nShpSize;
757 :
758 : // Set up write pointers.
759 0 : unsigned char *pabyPtr = *ppabyShape;
760 0 : unsigned char *pabyPtrM = bHasM ? pabyPtr + nShpSize - nShpZSize : nullptr;
761 :
762 0 : unsigned char *pabyPtrZ = nullptr;
763 0 : if (b3d)
764 : {
765 0 : if (bHasM)
766 0 : pabyPtrZ = pabyPtrM - nShpZSize;
767 : else
768 0 : pabyPtrZ = pabyPtr + nShpSize - nShpZSize;
769 : }
770 :
771 : /* -------------------------------------------------------------------- */
772 : /* Write in the Shape type number now */
773 : /* -------------------------------------------------------------------- */
774 0 : GUInt32 nGType = SHPT_NULL;
775 :
776 0 : switch (nOGRType)
777 : {
778 0 : case wkbPoint:
779 : {
780 0 : nGType = (b3d && bHasM) ? SHPT_POINTZM
781 : : (b3d) ? SHPT_POINTZ
782 : : (bHasM) ? SHPT_POINTM
783 : : SHPT_POINT;
784 0 : break;
785 : }
786 0 : case wkbMultiPoint:
787 : {
788 0 : nGType = (b3d && bHasM) ? SHPT_MULTIPOINTZM
789 : : (b3d) ? SHPT_MULTIPOINTZ
790 : : (bHasM) ? SHPT_MULTIPOINTM
791 : : SHPT_MULTIPOINT;
792 0 : break;
793 : }
794 0 : case wkbLineString:
795 : case wkbMultiLineString:
796 : {
797 0 : nGType = (b3d && bHasM) ? SHPT_ARCZM
798 : : (b3d) ? SHPT_ARCZ
799 : : (bHasM) ? SHPT_ARCM
800 : : SHPT_ARC;
801 0 : break;
802 : }
803 0 : case wkbPolygon:
804 : case wkbMultiPolygon:
805 : {
806 0 : nGType = (b3d && bHasM) ? SHPT_POLYGONZM
807 : : (b3d) ? SHPT_POLYGONZ
808 : : (bHasM) ? SHPT_POLYGONM
809 : : SHPT_POLYGON;
810 0 : break;
811 : }
812 0 : default:
813 : {
814 0 : return OGRERR_UNSUPPORTED_OPERATION;
815 : }
816 : }
817 : // Write in the type number and advance the pointer.
818 : #ifdef WRITE_ARC_HACK
819 : nGType = SHPT_GENERALPOLYLINE | 0x20000000;
820 : #endif
821 :
822 0 : CPL_LSBPTR32(&nGType);
823 0 : memcpy(pabyPtr, &nGType, 4);
824 0 : pabyPtr += 4;
825 :
826 : /* -------------------------------------------------------------------- */
827 : /* POINT and POINTZ */
828 : /* -------------------------------------------------------------------- */
829 0 : if (nOGRType == wkbPoint)
830 : {
831 0 : auto poPoint = poGeom->toPoint();
832 0 : const double x = poPoint->getX();
833 0 : const double y = poPoint->getY();
834 :
835 : // Copy in the raw data.
836 0 : memcpy(pabyPtr, &x, 8);
837 0 : memcpy(pabyPtr + 8, &y, 8);
838 0 : if (b3d)
839 : {
840 0 : double z = poPoint->getZ();
841 0 : memcpy(pabyPtr + 8 + 8, &z, 8);
842 : }
843 0 : if (bHasM)
844 : {
845 0 : double m = poPoint->getM();
846 0 : memcpy(pabyPtr + 8 + ((b3d) ? 16 : 8), &m, 8);
847 : }
848 :
849 : // Swap if needed. Shape doubles always LSB.
850 : if constexpr (OGR_SWAP(wkbNDR))
851 : {
852 : CPL_SWAPDOUBLE(pabyPtr);
853 : CPL_SWAPDOUBLE(pabyPtr + 8);
854 : if (b3d)
855 : CPL_SWAPDOUBLE(pabyPtr + 8 + 8);
856 : if (bHasM)
857 : CPL_SWAPDOUBLE(pabyPtr + 8 + ((b3d) ? 16 : 8));
858 : }
859 :
860 0 : return OGRERR_NONE;
861 : }
862 :
863 : /* -------------------------------------------------------------------- */
864 : /* All the non-POINT types require an envelope next */
865 : /* -------------------------------------------------------------------- */
866 0 : OGREnvelope3D envelope;
867 0 : poGeom->getEnvelope(&envelope);
868 0 : memcpy(pabyPtr, &(envelope.MinX), 8);
869 0 : memcpy(pabyPtr + 8, &(envelope.MinY), 8);
870 0 : memcpy(pabyPtr + 8 + 8, &(envelope.MaxX), 8);
871 0 : memcpy(pabyPtr + 8 + 8 + 8, &(envelope.MaxY), 8);
872 :
873 : // Swap box if needed. Shape doubles are always LSB.
874 : if constexpr (OGR_SWAP(wkbNDR))
875 : {
876 : for (int i = 0; i < 4; i++)
877 : CPL_SWAPDOUBLE(pabyPtr + 8 * i);
878 : }
879 0 : pabyPtr += 32;
880 :
881 : // Write in the Z bounds at the end of the XY buffer.
882 0 : if (b3d)
883 : {
884 0 : memcpy(pabyPtrZ, &(envelope.MinZ), 8);
885 0 : memcpy(pabyPtrZ + 8, &(envelope.MaxZ), 8);
886 :
887 : // Swap Z bounds if necessary.
888 : if constexpr (OGR_SWAP(wkbNDR))
889 : {
890 : for (int i = 0; i < 2; i++)
891 : CPL_SWAPDOUBLE(pabyPtrZ + 8 * i);
892 : }
893 0 : pabyPtrZ += 16;
894 : }
895 :
896 : // Reserve space for the M bounds at the end of the XY buffer.
897 0 : GByte *pabyPtrMBounds = nullptr;
898 0 : double dfMinM = std::numeric_limits<double>::max();
899 0 : double dfMaxM = -dfMinM;
900 0 : if (bHasM)
901 : {
902 0 : pabyPtrMBounds = pabyPtrM;
903 0 : pabyPtrM += 16;
904 : }
905 :
906 : /* -------------------------------------------------------------------- */
907 : /* LINESTRING and LINESTRINGZ */
908 : /* -------------------------------------------------------------------- */
909 0 : if (nOGRType == wkbLineString)
910 : {
911 0 : auto poLine = poGeom->toLineString();
912 :
913 : // Write in the nparts (1).
914 0 : const GUInt32 nPartsLsb = CPL_LSBWORD32(nParts);
915 0 : memcpy(pabyPtr, &nPartsLsb, 4);
916 0 : pabyPtr += 4;
917 :
918 : // Write in the npoints.
919 0 : GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
920 0 : memcpy(pabyPtr, &nPointsLsb, 4);
921 0 : pabyPtr += 4;
922 :
923 : // Write in the part index (0).
924 0 : GUInt32 nPartIndex = 0;
925 0 : memcpy(pabyPtr, &nPartIndex, 4);
926 0 : pabyPtr += 4;
927 :
928 : // Write in the point data.
929 0 : poLine->getPoints(reinterpret_cast<OGRRawPoint *>(pabyPtr),
930 : reinterpret_cast<double *>(pabyPtrZ));
931 0 : if (bHasM)
932 : {
933 0 : for (GUInt32 k = 0; k < nPoints; k++)
934 : {
935 0 : double dfM = poLine->getM(k);
936 0 : memcpy(pabyPtrM + 8 * k, &dfM, 8);
937 0 : if (dfM < dfMinM)
938 0 : dfMinM = dfM;
939 0 : if (dfM > dfMaxM)
940 0 : dfMaxM = dfM;
941 : }
942 : }
943 :
944 : // Swap if necessary.
945 : if constexpr (OGR_SWAP(wkbNDR))
946 : {
947 : for (GUInt32 k = 0; k < nPoints; k++)
948 : {
949 : CPL_SWAPDOUBLE(pabyPtr + 16 * k);
950 : CPL_SWAPDOUBLE(pabyPtr + 16 * k + 8);
951 : if (b3d)
952 : CPL_SWAPDOUBLE(pabyPtrZ + 8 * k);
953 : if (bHasM)
954 : CPL_SWAPDOUBLE(pabyPtrM + 8 * k);
955 : }
956 : }
957 : }
958 : /* -------------------------------------------------------------------- */
959 : /* POLYGON and POLYGONZ */
960 : /* -------------------------------------------------------------------- */
961 0 : else if (nOGRType == wkbPolygon)
962 : {
963 0 : auto poPoly = poGeom->toPolygon();
964 :
965 : // Write in the part count.
966 0 : GUInt32 nPartsLsb = CPL_LSBWORD32(nParts);
967 0 : memcpy(pabyPtr, &nPartsLsb, 4);
968 0 : pabyPtr += 4;
969 :
970 : // Write in the total point count.
971 0 : GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
972 0 : memcpy(pabyPtr, &nPointsLsb, 4);
973 0 : pabyPtr += 4;
974 :
975 : /* --------------------------------------------------------------------
976 : */
977 : /* Now we have to visit each ring and write an index number into */
978 : /* the parts list, and the coordinates into the points list. */
979 : /* to do it in one pass, we will use three write pointers. */
980 : /* pabyPtr writes the part indexes */
981 : /* pabyPoints writes the xy coordinates */
982 : /* pabyPtrZ writes the z coordinates */
983 : /* --------------------------------------------------------------------
984 : */
985 :
986 : // Just past the partindex[nparts] array.
987 0 : unsigned char *pabyPoints = pabyPtr + 4 * nParts;
988 :
989 0 : int nPointIndexCount = 0;
990 :
991 0 : for (GUInt32 i = 0; i < nParts; i++)
992 : {
993 : // Check our Ring and condition it.
994 0 : std::unique_ptr<OGRLinearRing> poRing;
995 0 : if (i == 0)
996 : {
997 0 : poRing.reset(poPoly->getExteriorRing()->clone());
998 0 : assert(poRing);
999 : // Outer ring must be clockwise.
1000 0 : if (!poRing->isClockwise())
1001 0 : poRing->reversePoints();
1002 : }
1003 : else
1004 : {
1005 0 : poRing.reset(poPoly->getInteriorRing(i - 1)->clone());
1006 0 : assert(poRing);
1007 : // Inner rings should be anti-clockwise.
1008 0 : if (poRing->isClockwise())
1009 0 : poRing->reversePoints();
1010 : }
1011 :
1012 0 : int nRingNumPoints = poRing->getNumPoints();
1013 :
1014 : #ifndef WRITE_ARC_HACK
1015 : // Cannot write un-closed rings to shape.
1016 0 : if (nRingNumPoints <= 2 || !poRing->get_IsClosed())
1017 0 : return OGRERR_FAILURE;
1018 : #endif
1019 :
1020 : // Write in the part index.
1021 0 : GUInt32 nPartIndex = CPL_LSBWORD32(nPointIndexCount);
1022 0 : memcpy(pabyPtr, &nPartIndex, 4);
1023 :
1024 : // Write in the point data.
1025 0 : poRing->getPoints(reinterpret_cast<OGRRawPoint *>(pabyPoints),
1026 : reinterpret_cast<double *>(pabyPtrZ));
1027 0 : if (bHasM)
1028 : {
1029 0 : for (int k = 0; k < nRingNumPoints; k++)
1030 : {
1031 0 : double dfM = poRing->getM(k);
1032 0 : memcpy(pabyPtrM + 8 * k, &dfM, 8);
1033 0 : if (dfM < dfMinM)
1034 0 : dfMinM = dfM;
1035 0 : if (dfM > dfMaxM)
1036 0 : dfMaxM = dfM;
1037 : }
1038 : }
1039 :
1040 : // Swap if necessary.
1041 : if constexpr (OGR_SWAP(wkbNDR))
1042 : {
1043 : for (int k = 0; k < nRingNumPoints; k++)
1044 : {
1045 : CPL_SWAPDOUBLE(pabyPoints + 16 * k);
1046 : CPL_SWAPDOUBLE(pabyPoints + 16 * k + 8);
1047 : if (b3d)
1048 : CPL_SWAPDOUBLE(pabyPtrZ + 8 * k);
1049 : if (bHasM)
1050 : CPL_SWAPDOUBLE(pabyPtrM + 8 * k);
1051 : }
1052 : }
1053 :
1054 0 : nPointIndexCount += nRingNumPoints;
1055 : // Advance the write pointers.
1056 0 : pabyPtr += 4;
1057 0 : pabyPoints += 16 * nRingNumPoints;
1058 0 : if (b3d)
1059 0 : pabyPtrZ += 8 * nRingNumPoints;
1060 0 : if (bHasM)
1061 0 : pabyPtrM += 8 * nRingNumPoints;
1062 : }
1063 : }
1064 : /* -------------------------------------------------------------------- */
1065 : /* MULTIPOINT and MULTIPOINTZ */
1066 : /* -------------------------------------------------------------------- */
1067 0 : else if (nOGRType == wkbMultiPoint)
1068 : {
1069 0 : auto poMPoint = poGeom->toMultiPoint();
1070 :
1071 : // Write in the total point count.
1072 0 : GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
1073 0 : memcpy(pabyPtr, &nPointsLsb, 4);
1074 0 : pabyPtr += 4;
1075 :
1076 : /* --------------------------------------------------------------------
1077 : */
1078 : /* Now we have to visit each point write it into the points list */
1079 : /* We will use two write pointers. */
1080 : /* pabyPtr writes the xy coordinates */
1081 : /* pabyPtrZ writes the z coordinates */
1082 : /* --------------------------------------------------------------------
1083 : */
1084 :
1085 0 : for (auto &&poPt : poMPoint)
1086 : {
1087 : // Skip empties.
1088 0 : if (poPt->IsEmpty())
1089 0 : continue;
1090 :
1091 : // Write the coordinates.
1092 0 : double x = poPt->getX();
1093 0 : double y = poPt->getY();
1094 0 : memcpy(pabyPtr, &x, 8);
1095 0 : memcpy(pabyPtr + 8, &y, 8);
1096 0 : if (b3d)
1097 : {
1098 0 : double z = poPt->getZ();
1099 0 : memcpy(pabyPtrZ, &z, 8);
1100 : }
1101 0 : if (bHasM)
1102 : {
1103 0 : double dfM = poPt->getM();
1104 0 : memcpy(pabyPtrM, &dfM, 8);
1105 0 : if (dfM < dfMinM)
1106 0 : dfMinM = dfM;
1107 0 : if (dfM > dfMaxM)
1108 0 : dfMaxM = dfM;
1109 : }
1110 :
1111 : // Swap if necessary.
1112 : if constexpr (OGR_SWAP(wkbNDR))
1113 : {
1114 : CPL_SWAPDOUBLE(pabyPtr);
1115 : CPL_SWAPDOUBLE(pabyPtr + 8);
1116 : if (b3d)
1117 : CPL_SWAPDOUBLE(pabyPtrZ);
1118 : if (bHasM)
1119 : CPL_SWAPDOUBLE(pabyPtrM);
1120 : }
1121 :
1122 : // Advance the write pointers.
1123 0 : pabyPtr += 16;
1124 0 : if (b3d)
1125 0 : pabyPtrZ += 8;
1126 0 : if (bHasM)
1127 0 : pabyPtrM += 8;
1128 : }
1129 : }
1130 :
1131 : /* -------------------------------------------------------------------- */
1132 : /* MULTILINESTRING and MULTILINESTRINGZ */
1133 : /* -------------------------------------------------------------------- */
1134 0 : else if (nOGRType == wkbMultiLineString)
1135 : {
1136 0 : auto poMLine = poGeom->toMultiLineString();
1137 :
1138 : // Write in the part count.
1139 0 : GUInt32 nPartsLsb = CPL_LSBWORD32(nParts);
1140 0 : memcpy(pabyPtr, &nPartsLsb, 4);
1141 0 : pabyPtr += 4;
1142 :
1143 : // Write in the total point count.
1144 0 : GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
1145 0 : memcpy(pabyPtr, &nPointsLsb, 4);
1146 0 : pabyPtr += 4;
1147 :
1148 : // Just past the partindex[nparts] array.
1149 0 : unsigned char *pabyPoints = pabyPtr + 4 * nParts;
1150 :
1151 0 : int nPointIndexCount = 0;
1152 :
1153 0 : for (auto &&poLine : poMLine)
1154 : {
1155 : // Skip empties.
1156 0 : if (poLine->IsEmpty())
1157 0 : continue;
1158 :
1159 0 : int nLineNumPoints = poLine->getNumPoints();
1160 :
1161 : // Write in the part index.
1162 0 : GUInt32 nPartIndex = CPL_LSBWORD32(nPointIndexCount);
1163 0 : memcpy(pabyPtr, &nPartIndex, 4);
1164 :
1165 : // Write in the point data.
1166 0 : poLine->getPoints(reinterpret_cast<OGRRawPoint *>(pabyPoints),
1167 : reinterpret_cast<double *>(pabyPtrZ));
1168 0 : if (bHasM)
1169 : {
1170 0 : for (int k = 0; k < nLineNumPoints; k++)
1171 : {
1172 0 : double dfM = poLine->getM(k);
1173 0 : memcpy(pabyPtrM + 8 * k, &dfM, 8);
1174 0 : if (dfM < dfMinM)
1175 0 : dfMinM = dfM;
1176 0 : if (dfM > dfMaxM)
1177 0 : dfMaxM = dfM;
1178 : }
1179 : }
1180 :
1181 : // Swap if necessary.
1182 : if constexpr (OGR_SWAP(wkbNDR))
1183 : {
1184 : for (int k = 0; k < nLineNumPoints; k++)
1185 : {
1186 : CPL_SWAPDOUBLE(pabyPoints + 16 * k);
1187 : CPL_SWAPDOUBLE(pabyPoints + 16 * k + 8);
1188 : if (b3d)
1189 : CPL_SWAPDOUBLE(pabyPtrZ + 8 * k);
1190 : if (bHasM)
1191 : CPL_SWAPDOUBLE(pabyPtrM + 8 * k);
1192 : }
1193 : }
1194 :
1195 0 : nPointIndexCount += nLineNumPoints;
1196 :
1197 : // Advance the write pointers.
1198 0 : pabyPtr += 4;
1199 0 : pabyPoints += 16 * nLineNumPoints;
1200 0 : if (b3d)
1201 0 : pabyPtrZ += 8 * nLineNumPoints;
1202 0 : if (bHasM)
1203 0 : pabyPtrM += 8 * nLineNumPoints;
1204 : }
1205 : }
1206 : /* -------------------------------------------------------------------- */
1207 : /* MULTIPOLYGON and MULTIPOLYGONZ */
1208 : /* -------------------------------------------------------------------- */
1209 : else // if( nOGRType == wkbMultiPolygon )
1210 : {
1211 0 : auto poMPoly = poGeom->toMultiPolygon();
1212 :
1213 : // Write in the part count.
1214 0 : GUInt32 nPartsLsb = CPL_LSBWORD32(nParts);
1215 0 : memcpy(pabyPtr, &nPartsLsb, 4);
1216 0 : pabyPtr += 4;
1217 :
1218 : // Write in the total point count.
1219 0 : GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
1220 0 : memcpy(pabyPtr, &nPointsLsb, 4);
1221 0 : pabyPtr += 4;
1222 :
1223 : /* --------------------------------------------------------------------
1224 : */
1225 : /* Now we have to visit each ring and write an index number into */
1226 : /* the parts list, and the coordinates into the points list. */
1227 : /* to do it in one pass, we will use three write pointers. */
1228 : /* pabyPtr writes the part indexes */
1229 : /* pabyPoints writes the xy coordinates */
1230 : /* pabyPtrZ writes the z coordinates */
1231 : /* --------------------------------------------------------------------
1232 : */
1233 :
1234 : // Just past the partindex[nparts] array.
1235 0 : unsigned char *pabyPoints = pabyPtr + 4 * nParts;
1236 :
1237 0 : int nPointIndexCount = 0;
1238 :
1239 0 : for (auto &&poPoly : poMPoly)
1240 : {
1241 : // Skip empties.
1242 0 : if (poPoly->IsEmpty())
1243 0 : continue;
1244 :
1245 0 : int nRings = 1 + poPoly->getNumInteriorRings();
1246 :
1247 0 : for (int j = 0; j < nRings; j++)
1248 : {
1249 : // Check our Ring and condition it.
1250 0 : std::unique_ptr<OGRLinearRing> poRing;
1251 0 : if (j == 0)
1252 : {
1253 0 : poRing.reset(poPoly->getExteriorRing()->clone());
1254 0 : assert(poRing != nullptr);
1255 : // Outer ring must be clockwise.
1256 0 : if (!poRing->isClockwise())
1257 0 : poRing->reversePoints();
1258 : }
1259 : else
1260 : {
1261 0 : poRing.reset(poPoly->getInteriorRing(j - 1)->clone());
1262 0 : assert(poRing != nullptr);
1263 : // Inner rings should be anti-clockwise.
1264 0 : if (poRing->isClockwise())
1265 0 : poRing->reversePoints();
1266 : }
1267 :
1268 0 : int nRingNumPoints = poRing->getNumPoints();
1269 :
1270 : // Cannot write closed rings to shape.
1271 0 : if (nRingNumPoints <= 2 || !poRing->get_IsClosed())
1272 0 : return OGRERR_FAILURE;
1273 :
1274 : // Write in the part index.
1275 0 : GUInt32 nPartIndex = CPL_LSBWORD32(nPointIndexCount);
1276 0 : memcpy(pabyPtr, &nPartIndex, 4);
1277 :
1278 : // Write in the point data.
1279 0 : poRing->getPoints(reinterpret_cast<OGRRawPoint *>(pabyPoints),
1280 : reinterpret_cast<double *>(pabyPtrZ));
1281 0 : if (bHasM)
1282 : {
1283 0 : for (int k = 0; k < nRingNumPoints; k++)
1284 : {
1285 0 : double dfM = poRing->getM(k);
1286 0 : memcpy(pabyPtrM + 8 * k, &dfM, 8);
1287 0 : if (dfM < dfMinM)
1288 0 : dfMinM = dfM;
1289 0 : if (dfM > dfMaxM)
1290 0 : dfMaxM = dfM;
1291 : }
1292 : }
1293 :
1294 : // Swap if necessary.
1295 : if constexpr (OGR_SWAP(wkbNDR))
1296 : {
1297 : for (int k = 0; k < nRingNumPoints; k++)
1298 : {
1299 : CPL_SWAPDOUBLE(pabyPoints + 16 * k);
1300 : CPL_SWAPDOUBLE(pabyPoints + 16 * k + 8);
1301 : if (b3d)
1302 : CPL_SWAPDOUBLE(pabyPtrZ + 8 * k);
1303 : if (bHasM)
1304 : CPL_SWAPDOUBLE(pabyPtrM + 8 * k);
1305 : }
1306 : }
1307 :
1308 0 : nPointIndexCount += nRingNumPoints;
1309 : // Advance the write pointers.
1310 0 : pabyPtr += 4;
1311 0 : pabyPoints += 16 * nRingNumPoints;
1312 0 : if (b3d)
1313 0 : pabyPtrZ += 8 * nRingNumPoints;
1314 0 : if (bHasM)
1315 0 : pabyPtrM += 8 * nRingNumPoints;
1316 : }
1317 : }
1318 : }
1319 :
1320 0 : if (bHasM)
1321 : {
1322 0 : if (dfMinM > dfMaxM)
1323 : {
1324 0 : dfMinM = 0.0;
1325 0 : dfMaxM = 0.0;
1326 : }
1327 0 : memcpy(pabyPtrMBounds, &(dfMinM), 8);
1328 0 : memcpy(pabyPtrMBounds + 8, &(dfMaxM), 8);
1329 :
1330 : // Swap M bounds if necessary.
1331 : if constexpr (OGR_SWAP(wkbNDR))
1332 : {
1333 : for (int i = 0; i < 2; i++)
1334 : CPL_SWAPDOUBLE(pabyPtrMBounds + 8 * i);
1335 : }
1336 : }
1337 :
1338 0 : return OGRERR_NONE;
1339 : }
1340 :
1341 : /************************************************************************/
1342 : /* OGRCreateMultiPatch() */
1343 : /************************************************************************/
1344 :
1345 36 : OGRErr OGRCreateMultiPatch(const OGRGeometry *poGeomConst,
1346 : int bAllowSHPTTriangle, int &nParts,
1347 : std::vector<int> &anPartStart,
1348 : std::vector<int> &anPartType, int &nPoints,
1349 : std::vector<OGRRawPoint> &aoPoints,
1350 : std::vector<double> &adfZ)
1351 : {
1352 36 : const OGRwkbGeometryType eType = wkbFlatten(poGeomConst->getGeometryType());
1353 36 : if (eType != wkbPolygon && eType != wkbTriangle &&
1354 32 : eType != wkbMultiPolygon && eType != wkbMultiSurface &&
1355 16 : eType != wkbTIN && eType != wkbPolyhedralSurface &&
1356 : eType != wkbGeometryCollection)
1357 : {
1358 0 : return OGRERR_UNSUPPORTED_OPERATION;
1359 : }
1360 :
1361 72 : std::unique_ptr<OGRGeometry> poGeom(poGeomConst->clone());
1362 36 : poGeom->closeRings();
1363 :
1364 36 : OGRMultiPolygon *poMPoly = nullptr;
1365 36 : std::unique_ptr<OGRGeometry> poGeomToDelete;
1366 36 : if (eType == wkbMultiPolygon)
1367 1 : poMPoly = poGeom->toMultiPolygon();
1368 : else
1369 : {
1370 70 : poGeomToDelete = std::unique_ptr<OGRGeometry>(
1371 70 : OGRGeometryFactory::forceToMultiPolygon(poGeom->clone()));
1372 70 : if (poGeomToDelete.get() &&
1373 35 : wkbFlatten(poGeomToDelete->getGeometryType()) == wkbMultiPolygon)
1374 : {
1375 32 : poMPoly = poGeomToDelete->toMultiPolygon();
1376 : }
1377 : }
1378 36 : if (poMPoly == nullptr)
1379 : {
1380 3 : return OGRERR_UNSUPPORTED_OPERATION;
1381 : }
1382 :
1383 33 : nParts = 0;
1384 33 : anPartStart.clear();
1385 33 : anPartType.clear();
1386 33 : nPoints = 0;
1387 33 : aoPoints.clear();
1388 33 : adfZ.clear();
1389 33 : int nBeginLastPart = 0;
1390 133 : for (const auto poPoly : *poMPoly)
1391 : {
1392 : // Skip empties.
1393 100 : if (poPoly->IsEmpty())
1394 0 : continue;
1395 :
1396 100 : const int nRings = poPoly->getNumInteriorRings() + 1;
1397 100 : const OGRLinearRing *poRing = poPoly->getExteriorRing();
1398 100 : if (nRings == 1 && poRing->getNumPoints() == 4)
1399 : {
1400 90 : int nCorrectedPoints = nPoints;
1401 95 : if (nParts > 0 && anPartType[nParts - 1] == SHPP_OUTERRING &&
1402 5 : nPoints - anPartStart[nParts - 1] == 4)
1403 : {
1404 5 : nCorrectedPoints--;
1405 : }
1406 :
1407 237 : if (nParts > 0 &&
1408 57 : ((anPartType[nParts - 1] == SHPP_TRIANGLES &&
1409 31 : nPoints - anPartStart[nParts - 1] == 3) ||
1410 26 : (anPartType[nParts - 1] == SHPP_OUTERRING &&
1411 5 : nPoints - anPartStart[nParts - 1] == 4) ||
1412 21 : anPartType[nParts - 1] == SHPP_TRIFAN) &&
1413 47 : poRing->getX(0) == aoPoints[nBeginLastPart].x &&
1414 34 : poRing->getY(0) == aoPoints[nBeginLastPart].y &&
1415 13 : poRing->getZ(0) == adfZ[nBeginLastPart] &&
1416 13 : poRing->getX(1) == aoPoints[nCorrectedPoints - 1].x &&
1417 159 : poRing->getY(1) == aoPoints[nCorrectedPoints - 1].y &&
1418 12 : poRing->getZ(1) == adfZ[nCorrectedPoints - 1])
1419 : {
1420 12 : nPoints = nCorrectedPoints;
1421 12 : anPartType[nParts - 1] = SHPP_TRIFAN;
1422 :
1423 12 : aoPoints.resize(nPoints + 1);
1424 12 : adfZ.resize(nPoints + 1);
1425 12 : aoPoints[nPoints].x = poRing->getX(2);
1426 12 : aoPoints[nPoints].y = poRing->getY(2);
1427 12 : adfZ[nPoints] = poRing->getZ(2);
1428 12 : nPoints++;
1429 : }
1430 201 : else if (nParts > 0 &&
1431 45 : ((anPartType[nParts - 1] == SHPP_TRIANGLES &&
1432 20 : nPoints - anPartStart[nParts - 1] == 3) ||
1433 25 : (anPartType[nParts - 1] == SHPP_OUTERRING &&
1434 4 : nPoints - anPartStart[nParts - 1] == 4) ||
1435 21 : anPartType[nParts - 1] == SHPP_TRISTRIP) &&
1436 34 : poRing->getX(0) == aoPoints[nCorrectedPoints - 2].x &&
1437 22 : poRing->getY(0) == aoPoints[nCorrectedPoints - 2].y &&
1438 21 : poRing->getZ(0) == adfZ[nCorrectedPoints - 2] &&
1439 21 : poRing->getX(1) == aoPoints[nCorrectedPoints - 1].x &&
1440 144 : poRing->getY(1) == aoPoints[nCorrectedPoints - 1].y &&
1441 21 : poRing->getZ(1) == adfZ[nCorrectedPoints - 1])
1442 : {
1443 21 : nPoints = nCorrectedPoints;
1444 21 : anPartType[nParts - 1] = SHPP_TRISTRIP;
1445 :
1446 21 : aoPoints.resize(nPoints + 1);
1447 21 : adfZ.resize(nPoints + 1);
1448 21 : aoPoints[nPoints].x = poRing->getX(2);
1449 21 : aoPoints[nPoints].y = poRing->getY(2);
1450 21 : adfZ[nPoints] = poRing->getZ(2);
1451 21 : nPoints++;
1452 : }
1453 : else
1454 : {
1455 57 : if (nParts == 0 || anPartType[nParts - 1] != SHPP_TRIANGLES ||
1456 : !bAllowSHPTTriangle)
1457 : {
1458 57 : nBeginLastPart = nPoints;
1459 :
1460 57 : anPartStart.resize(nParts + 1);
1461 57 : anPartType.resize(nParts + 1);
1462 57 : anPartStart[nParts] = nPoints;
1463 57 : anPartType[nParts] =
1464 57 : bAllowSHPTTriangle ? SHPP_TRIANGLES : SHPP_OUTERRING;
1465 57 : nParts++;
1466 : }
1467 :
1468 57 : aoPoints.resize(nPoints + 4);
1469 57 : adfZ.resize(nPoints + 4);
1470 285 : for (int i = 0; i < 4; i++)
1471 : {
1472 228 : aoPoints[nPoints + i].x = poRing->getX(i);
1473 228 : aoPoints[nPoints + i].y = poRing->getY(i);
1474 228 : adfZ[nPoints + i] = poRing->getZ(i);
1475 : }
1476 57 : nPoints += bAllowSHPTTriangle ? 3 : 4;
1477 : }
1478 : }
1479 : else
1480 : {
1481 10 : anPartStart.resize(nParts + nRings);
1482 10 : anPartType.resize(nParts + nRings);
1483 :
1484 30 : for (int i = 0; i < nRings; i++)
1485 : {
1486 20 : anPartStart[nParts + i] = nPoints;
1487 20 : if (i == 0)
1488 : {
1489 10 : poRing = poPoly->getExteriorRing();
1490 10 : anPartType[nParts + i] = SHPP_OUTERRING;
1491 : }
1492 : else
1493 : {
1494 10 : poRing = poPoly->getInteriorRing(i - 1);
1495 10 : anPartType[nParts + i] = SHPP_INNERRING;
1496 : }
1497 20 : aoPoints.resize(nPoints + poRing->getNumPoints());
1498 20 : adfZ.resize(nPoints + poRing->getNumPoints());
1499 120 : for (int k = 0; k < poRing->getNumPoints(); k++)
1500 : {
1501 100 : aoPoints[nPoints + k].x = poRing->getX(k);
1502 100 : aoPoints[nPoints + k].y = poRing->getY(k);
1503 100 : adfZ[nPoints + k] = poRing->getZ(k);
1504 : }
1505 20 : nPoints += poRing->getNumPoints();
1506 : }
1507 :
1508 10 : nParts += nRings;
1509 : }
1510 : }
1511 :
1512 33 : if (nParts == 1 && anPartType[0] == SHPP_OUTERRING && nPoints == 4)
1513 : {
1514 8 : anPartType[0] = SHPP_TRIFAN;
1515 8 : nPoints = 3;
1516 : }
1517 :
1518 33 : return OGRERR_NONE;
1519 : }
1520 :
1521 : /************************************************************************/
1522 : /* OGRWriteMultiPatchToShapeBin() */
1523 : /************************************************************************/
1524 :
1525 0 : OGRErr OGRWriteMultiPatchToShapeBin(const OGRGeometry *poGeom,
1526 : GByte **ppabyShape, int *pnBytes)
1527 : {
1528 0 : int nParts = 0;
1529 0 : std::vector<int> anPartStart;
1530 0 : std::vector<int> anPartType;
1531 0 : int nPoints = 0;
1532 0 : std::vector<OGRRawPoint> aoPoints;
1533 0 : std::vector<double> adfZ;
1534 0 : OGRErr eErr = OGRCreateMultiPatch(poGeom, TRUE, nParts, anPartStart,
1535 : anPartType, nPoints, aoPoints, adfZ);
1536 0 : if (eErr != OGRERR_NONE)
1537 0 : return eErr;
1538 :
1539 : const bool bOmitZ =
1540 0 : !poGeom->Is3D() &&
1541 0 : CPLTestBool(CPLGetConfigOption("OGR_MULTIPATCH_OMIT_Z", "NO"));
1542 :
1543 0 : int nShpSize = 4; // All types start with integer type number.
1544 0 : nShpSize += 16 * 2; // xy bbox.
1545 0 : nShpSize += 4; // nparts.
1546 0 : nShpSize += 4; // npoints.
1547 0 : nShpSize += 4 * nParts; // panPartStart[nparts].
1548 0 : nShpSize += 4 * nParts; // panPartType[nparts].
1549 0 : nShpSize += 8 * 2 * nPoints; // xy points.
1550 0 : if (!bOmitZ)
1551 : {
1552 0 : nShpSize += 16; // z bbox.
1553 0 : nShpSize += 8 * nPoints; // z points.
1554 : }
1555 :
1556 0 : *pnBytes = nShpSize;
1557 0 : *ppabyShape = static_cast<GByte *>(CPLMalloc(nShpSize));
1558 :
1559 0 : GByte *pabyPtr = *ppabyShape;
1560 :
1561 : // Write in the type number and advance the pointer.
1562 0 : GUInt32 nGType = bOmitZ ? CPL_LSBWORD32(SHPT_GENERALMULTIPATCH)
1563 : : CPL_LSBWORD32(SHPT_MULTIPATCH);
1564 0 : memcpy(pabyPtr, &nGType, 4);
1565 0 : pabyPtr += 4;
1566 :
1567 0 : OGREnvelope3D envelope;
1568 0 : poGeom->getEnvelope(&envelope);
1569 0 : memcpy(pabyPtr, &(envelope.MinX), 8);
1570 0 : memcpy(pabyPtr + 8, &(envelope.MinY), 8);
1571 0 : memcpy(pabyPtr + 8 + 8, &(envelope.MaxX), 8);
1572 0 : memcpy(pabyPtr + 8 + 8 + 8, &(envelope.MaxY), 8);
1573 :
1574 : // Swap box if needed. Shape doubles are always LSB.
1575 : if constexpr (OGR_SWAP(wkbNDR))
1576 : {
1577 : for (int i = 0; i < 4; i++)
1578 : CPL_SWAPDOUBLE(pabyPtr + 8 * i);
1579 : }
1580 0 : pabyPtr += 32;
1581 :
1582 : // Write in the part count.
1583 0 : GUInt32 nPartsLsb = CPL_LSBWORD32(nParts);
1584 0 : memcpy(pabyPtr, &nPartsLsb, 4);
1585 0 : pabyPtr += 4;
1586 :
1587 : // Write in the total point count.
1588 0 : GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
1589 0 : memcpy(pabyPtr, &nPointsLsb, 4);
1590 0 : pabyPtr += 4;
1591 :
1592 0 : for (int i = 0; i < nParts; i++)
1593 : {
1594 0 : int nPartStart = CPL_LSBWORD32(anPartStart[i]);
1595 0 : memcpy(pabyPtr, &nPartStart, 4);
1596 0 : pabyPtr += 4;
1597 : }
1598 0 : for (int i = 0; i < nParts; i++)
1599 : {
1600 0 : int nPartType = CPL_LSBWORD32(anPartType[i]);
1601 0 : memcpy(pabyPtr, &nPartType, 4);
1602 0 : pabyPtr += 4;
1603 : }
1604 :
1605 0 : if (!aoPoints.empty())
1606 0 : memcpy(pabyPtr, aoPoints.data(), 2 * 8 * nPoints);
1607 :
1608 : // Swap box if needed. Shape doubles are always LSB.
1609 : if constexpr (OGR_SWAP(wkbNDR))
1610 : {
1611 : for (int i = 0; i < 2 * nPoints; i++)
1612 : CPL_SWAPDOUBLE(pabyPtr + 8 * i);
1613 : }
1614 0 : pabyPtr += 2 * 8 * nPoints;
1615 :
1616 0 : if (!bOmitZ)
1617 : {
1618 0 : memcpy(pabyPtr, &(envelope.MinZ), 8);
1619 0 : memcpy(pabyPtr + 8, &(envelope.MaxZ), 8);
1620 : if constexpr (OGR_SWAP(wkbNDR))
1621 : {
1622 : for (int i = 0; i < 2; i++)
1623 : CPL_SWAPDOUBLE(pabyPtr + 8 * i);
1624 : }
1625 0 : pabyPtr += 16;
1626 :
1627 0 : if (!adfZ.empty())
1628 0 : memcpy(pabyPtr, adfZ.data(), 8 * nPoints);
1629 : // Swap box if needed. Shape doubles are always LSB.
1630 : if constexpr (OGR_SWAP(wkbNDR))
1631 : {
1632 : for (int i = 0; i < nPoints; i++)
1633 : CPL_SWAPDOUBLE(pabyPtr + 8 * i);
1634 : }
1635 : // pabyPtr += 8 * nPoints;
1636 : }
1637 :
1638 0 : return OGRERR_NONE;
1639 : }
1640 :
1641 : /************************************************************************/
1642 : /* GetAngleOnEllipse() */
1643 : /************************************************************************/
1644 :
1645 : // Return the angle in deg [0, 360] of dfArcX,dfArcY regarding the
1646 : // ellipse semi-major axis.
1647 26 : static double GetAngleOnEllipse(double dfPointOnArcX, double dfPointOnArcY,
1648 : double dfCenterX, double dfCenterY,
1649 : double dfRotationDeg, // Ellipse rotation.
1650 : double dfSemiMajor, double dfSemiMinor)
1651 : {
1652 : // Invert the following equation where cosA, sinA are unknown:
1653 : // dfPointOnArcX-dfCenterX = cosA*M*cosRot + sinA*m*sinRot
1654 : // dfPointOnArcY-dfCenterY = -cosA*M*sinRot + sinA*m*cosRot
1655 :
1656 26 : if (dfSemiMajor == 0.0 || dfSemiMinor == 0.0)
1657 0 : return 0.0;
1658 26 : const double dfRotationRadians = dfRotationDeg * M_PI / 180.0;
1659 26 : const double dfCosRot = cos(dfRotationRadians);
1660 26 : const double dfSinRot = sin(dfRotationRadians);
1661 26 : const double dfDeltaX = dfPointOnArcX - dfCenterX;
1662 26 : const double dfDeltaY = dfPointOnArcY - dfCenterY;
1663 26 : const double dfCosA =
1664 26 : (dfCosRot * dfDeltaX - dfSinRot * dfDeltaY) / dfSemiMajor;
1665 26 : const double dfSinA =
1666 26 : (dfSinRot * dfDeltaX + dfCosRot * dfDeltaY) / dfSemiMinor;
1667 : // We could check that dfCosA^2 + dfSinA^2 ~= 1 to verify that the point
1668 : // is on the ellipse.
1669 26 : const double dfAngle = atan2(dfSinA, dfCosA) / M_PI * 180;
1670 26 : if (dfAngle < -180)
1671 0 : return dfAngle + 360;
1672 26 : return dfAngle;
1673 : }
1674 :
1675 : /************************************************************************/
1676 : /* OGRShapeCreateCompoundCurve() */
1677 : /************************************************************************/
1678 :
1679 122 : static OGRCurve *OGRShapeCreateCompoundCurve(int nPartStartIdx, int nPartPoints,
1680 : const CurveSegment *pasCurves,
1681 : int nCurves, int nFirstCurveIdx,
1682 : /* const */ double *padfX,
1683 : /* const */ double *padfY,
1684 : /* const */ double *padfZ,
1685 : /* const */ double *padfM,
1686 : int *pnLastCurveIdx)
1687 : {
1688 244 : auto poCC = std::make_unique<OGRCompoundCurve>();
1689 122 : int nLastPointIdx = nPartStartIdx;
1690 122 : bool bHasCircularArcs = false;
1691 122 : int i = nFirstCurveIdx; // Used after for.
1692 296 : for (; i < nCurves; i++)
1693 : {
1694 200 : const int nStartPointIdx = pasCurves[i].nStartPointIdx;
1695 :
1696 200 : if (nStartPointIdx < nPartStartIdx)
1697 : {
1698 : // Shouldn't happen normally, but who knows.
1699 0 : continue;
1700 : }
1701 :
1702 : // For a multi-part geometry, stop when the start index of the curve
1703 : // exceeds the last point index of the part
1704 200 : if (nStartPointIdx >= nPartStartIdx + nPartPoints)
1705 : {
1706 26 : if (pnLastCurveIdx)
1707 26 : *pnLastCurveIdx = i;
1708 26 : break;
1709 : }
1710 :
1711 : // Add linear segments between end of last curve portion (or beginning
1712 : // of the part) and start of current curve.
1713 174 : if (nStartPointIdx > nLastPointIdx)
1714 : {
1715 39 : OGRLineString *poLine = new OGRLineString();
1716 43 : poLine->setPoints(
1717 39 : nStartPointIdx - nLastPointIdx + 1, padfX + nLastPointIdx,
1718 39 : padfY + nLastPointIdx,
1719 8 : padfZ != nullptr ? padfZ + nLastPointIdx : nullptr,
1720 4 : padfM != nullptr ? padfM + nLastPointIdx : nullptr);
1721 39 : poCC->addCurveDirectly(poLine);
1722 : }
1723 :
1724 174 : if (pasCurves[i].eType == CURVE_ARC_INTERIOR_POINT &&
1725 113 : nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1726 : {
1727 113 : OGRPoint p1(padfX[nStartPointIdx], padfY[nStartPointIdx],
1728 26 : padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1729 113 : padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1730 113 : OGRPoint p2(pasCurves[i].u.ArcByIntermediatePoint.dfX,
1731 113 : pasCurves[i].u.ArcByIntermediatePoint.dfY,
1732 113 : padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0);
1733 113 : OGRPoint p3(padfX[nStartPointIdx + 1], padfY[nStartPointIdx + 1],
1734 26 : padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1735 113 : padfM != nullptr ? padfM[nStartPointIdx + 1] : 0.0);
1736 :
1737 : // Some software (like QGIS, see https://hub.qgis.org/issues/15116)
1738 : // do not like 3-point circles, so use a 5 point variant.
1739 113 : if (p1.getX() == p3.getX() && p1.getY() == p3.getY())
1740 : {
1741 8 : if (p1.getX() != p2.getX() || p1.getY() != p2.getY())
1742 : {
1743 8 : bHasCircularArcs = true;
1744 8 : OGRCircularString *poCS = new OGRCircularString();
1745 8 : const double dfCenterX = (p1.getX() + p2.getX()) / 2;
1746 8 : const double dfCenterY = (p1.getY() + p2.getY()) / 2;
1747 8 : poCS->addPoint(&p1);
1748 16 : OGRPoint pInterm1(dfCenterX - (p2.getY() - dfCenterY),
1749 8 : dfCenterY + (p1.getX() - dfCenterX),
1750 2 : padfZ != nullptr ? padfZ[nStartPointIdx]
1751 24 : : 0.0);
1752 8 : poCS->addPoint(&pInterm1);
1753 8 : poCS->addPoint(&p2);
1754 16 : OGRPoint pInterm2(dfCenterX + (p2.getY() - dfCenterY),
1755 8 : dfCenterY - (p1.getX() - dfCenterX),
1756 2 : padfZ != nullptr ? padfZ[nStartPointIdx]
1757 24 : : 0.0);
1758 8 : poCS->addPoint(&pInterm2);
1759 8 : poCS->addPoint(&p3);
1760 8 : poCS->set3D(padfZ != nullptr);
1761 8 : poCS->setMeasured(padfM != nullptr);
1762 8 : poCC->addCurveDirectly(poCS);
1763 : }
1764 : }
1765 : else
1766 : {
1767 105 : bHasCircularArcs = true;
1768 105 : OGRCircularString *poCS = new OGRCircularString();
1769 105 : poCS->addPoint(&p1);
1770 105 : poCS->addPoint(&p2);
1771 105 : poCS->addPoint(&p3);
1772 105 : poCS->set3D(padfZ != nullptr);
1773 105 : poCS->setMeasured(padfM != nullptr);
1774 105 : if (poCC->addCurveDirectly(poCS) != OGRERR_NONE)
1775 : {
1776 0 : delete poCS;
1777 0 : return nullptr;
1778 : }
1779 113 : }
1780 : }
1781 :
1782 61 : else if (pasCurves[i].eType == CURVE_ARC_CENTER_POINT &&
1783 12 : nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1784 : {
1785 12 : const double dfCenterX = pasCurves[i].u.ArcByCenterPoint.dfX;
1786 12 : const double dfCenterY = pasCurves[i].u.ArcByCenterPoint.dfY;
1787 12 : double dfDeltaY = padfY[nStartPointIdx] - dfCenterY;
1788 12 : double dfDeltaX = padfX[nStartPointIdx] - dfCenterX;
1789 12 : double dfAngleStart = atan2(dfDeltaY, dfDeltaX);
1790 12 : dfDeltaY = padfY[nStartPointIdx + 1] - dfCenterY;
1791 12 : dfDeltaX = padfX[nStartPointIdx + 1] - dfCenterX;
1792 12 : double dfAngleEnd = atan2(dfDeltaY, dfDeltaX);
1793 : // Note: This definition from center and 2 points may be
1794 : // not a circle.
1795 12 : double dfRadius = sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
1796 12 : if (pasCurves[i].u.ArcByCenterPoint.bIsCCW)
1797 : {
1798 4 : if (dfAngleStart >= dfAngleEnd)
1799 2 : dfAngleEnd += 2 * M_PI;
1800 : }
1801 : else
1802 : {
1803 8 : if (dfAngleStart <= dfAngleEnd)
1804 6 : dfAngleEnd -= 2 * M_PI;
1805 : }
1806 12 : const double dfMidAngle = (dfAngleStart + dfAngleEnd) / 2;
1807 12 : OGRPoint p1(padfX[nStartPointIdx], padfY[nStartPointIdx],
1808 0 : padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1809 24 : padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1810 12 : OGRPoint p2(dfCenterX + dfRadius * cos(dfMidAngle),
1811 12 : dfCenterY + dfRadius * sin(dfMidAngle),
1812 24 : padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0);
1813 12 : OGRPoint p3(padfX[nStartPointIdx + 1], padfY[nStartPointIdx + 1],
1814 0 : padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1815 24 : padfM != nullptr ? padfM[nStartPointIdx + 1] : 0.0);
1816 :
1817 12 : bHasCircularArcs = true;
1818 12 : OGRCircularString *poCS = new OGRCircularString();
1819 12 : poCS->addPoint(&p1);
1820 12 : poCS->addPoint(&p2);
1821 12 : poCS->addPoint(&p3);
1822 12 : poCS->set3D(padfZ != nullptr);
1823 12 : poCS->setMeasured(padfM != nullptr);
1824 24 : poCC->addCurveDirectly(poCS);
1825 : }
1826 :
1827 49 : else if (pasCurves[i].eType == CURVE_BEZIER &&
1828 36 : nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1829 : {
1830 36 : OGRLineString *poLine = new OGRLineString();
1831 36 : const double dfX0 = padfX[nStartPointIdx];
1832 36 : const double dfY0 = padfY[nStartPointIdx];
1833 36 : const double dfX1 = pasCurves[i].u.Bezier.dfX1;
1834 36 : const double dfY1 = pasCurves[i].u.Bezier.dfY1;
1835 36 : const double dfX2 = pasCurves[i].u.Bezier.dfX2;
1836 36 : const double dfY2 = pasCurves[i].u.Bezier.dfY2;
1837 36 : const double dfX3 = padfX[nStartPointIdx + 1];
1838 36 : const double dfY3 = padfY[nStartPointIdx + 1];
1839 36 : double dfStartAngle = atan2(dfY1 - dfY0, dfX1 - dfX0);
1840 36 : double dfEndAngle = atan2(dfY3 - dfY2, dfX3 - dfX2);
1841 36 : if (dfStartAngle + M_PI < dfEndAngle)
1842 2 : dfStartAngle += 2 * M_PI;
1843 34 : else if (dfEndAngle + M_PI < dfStartAngle)
1844 0 : dfEndAngle += 2 * M_PI;
1845 : const double dfStepSizeRad =
1846 36 : OGRGeometryFactory::GetDefaultArcStepSize() / 180.0 * M_PI;
1847 36 : const double dfLengthTangentStart =
1848 36 : (dfX1 - dfX0) * (dfX1 - dfX0) + (dfY1 - dfY0) * (dfY1 - dfY0);
1849 36 : const double dfLengthTangentEnd =
1850 36 : (dfX3 - dfX2) * (dfX3 - dfX2) + (dfY3 - dfY2) * (dfY3 - dfY2);
1851 36 : const double dfLength =
1852 36 : (dfX3 - dfX0) * (dfX3 - dfX0) + (dfY3 - dfY0) * (dfY3 - dfY0);
1853 : // Heuristics to compute number of steps: we take into account the
1854 : // angular difference between the start and end tangent. And we
1855 : // also take into account the relative length of the tangent vs
1856 : // the length of the straight segment
1857 : const int nSteps =
1858 : (dfLength < 1e-9)
1859 36 : ? 1
1860 36 : : static_cast<int>(std::min(
1861 72 : 1000.0,
1862 144 : ceil(std::max(2.0, fabs(dfEndAngle - dfStartAngle) /
1863 36 : dfStepSizeRad) *
1864 108 : std::max(1.0, 5.0 *
1865 36 : (dfLengthTangentStart +
1866 72 : dfLengthTangentEnd) /
1867 36 : dfLength))));
1868 36 : poLine->setNumPoints(nSteps + 1);
1869 84 : poLine->setPoint(0, dfX0, dfY0,
1870 24 : padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1871 24 : padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1872 1317 : for (int j = 1; j < nSteps; j++)
1873 : {
1874 1281 : const double t = static_cast<double>(j) / nSteps;
1875 : // Third-order Bezier interpolation.
1876 1281 : poLine->setPoint(
1877 : j,
1878 1281 : (1 - t) * (1 - t) * (1 - t) * dfX0 +
1879 1281 : 3 * (1 - t) * (1 - t) * t * dfX1 +
1880 1281 : 3 * (1 - t) * t * t * dfX2 + t * t * t * dfX3,
1881 1281 : (1 - t) * (1 - t) * (1 - t) * dfY0 +
1882 1281 : 3 * (1 - t) * (1 - t) * t * dfY1 +
1883 1281 : 3 * (1 - t) * t * t * dfY2 + t * t * t * dfY3);
1884 : }
1885 84 : poLine->setPoint(nSteps, dfX3, dfY3,
1886 24 : padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1887 24 : padfM != nullptr ? padfM[nStartPointIdx + 1]
1888 : : 0.0);
1889 36 : poLine->set3D(padfZ != nullptr);
1890 36 : poLine->setMeasured(padfM != nullptr);
1891 36 : if (poCC->addCurveDirectly(poLine) != OGRERR_NONE)
1892 : {
1893 0 : delete poLine;
1894 0 : return nullptr;
1895 36 : }
1896 : }
1897 :
1898 13 : else if (pasCurves[i].eType == CURVE_ELLIPSE_BY_CENTER &&
1899 13 : nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1900 : {
1901 13 : const double dfSemiMinor =
1902 13 : pasCurves[i].u.EllipseByCenter.dfSemiMajor *
1903 13 : pasCurves[i].u.EllipseByCenter.dfRatioSemiMinor;
1904 : // Different sign conventions between ext shape
1905 : // (trigonometric, CCW) and approximateArcAngles (CW).
1906 13 : const double dfRotationDeg =
1907 13 : -pasCurves[i].u.EllipseByCenter.dfRotationDeg;
1908 13 : const double dfAngleStart = GetAngleOnEllipse(
1909 13 : padfX[nStartPointIdx], padfY[nStartPointIdx],
1910 13 : pasCurves[i].u.EllipseByCenter.dfX,
1911 13 : pasCurves[i].u.EllipseByCenter.dfY, dfRotationDeg,
1912 13 : pasCurves[i].u.EllipseByCenter.dfSemiMajor, dfSemiMinor);
1913 13 : const double dfAngleEnd = GetAngleOnEllipse(
1914 13 : padfX[nStartPointIdx + 1], padfY[nStartPointIdx + 1],
1915 13 : pasCurves[i].u.EllipseByCenter.dfX,
1916 13 : pasCurves[i].u.EllipseByCenter.dfY, dfRotationDeg,
1917 13 : pasCurves[i].u.EllipseByCenter.dfSemiMajor, dfSemiMinor);
1918 : // CPLDebug("OGR", "Start angle=%f, End angle=%f",
1919 : // dfAngleStart, dfAngleEnd);
1920 : // Approximatearcangles() use CW.
1921 13 : double dfAngleStartForApprox = -dfAngleStart;
1922 13 : double dfAngleEndForApprox = -dfAngleEnd;
1923 13 : if (pasCurves[i].u.EllipseByCenter.bIsComplete)
1924 : {
1925 9 : dfAngleEndForApprox = dfAngleStartForApprox + 360;
1926 : }
1927 4 : else if (pasCurves[i].u.EllipseByCenter.bIsMinor)
1928 : {
1929 4 : if (dfAngleEndForApprox > dfAngleStartForApprox + 180)
1930 : {
1931 0 : dfAngleEndForApprox -= 360;
1932 : }
1933 4 : else if (dfAngleEndForApprox < dfAngleStartForApprox - 180)
1934 : {
1935 0 : dfAngleEndForApprox += 360;
1936 : }
1937 : }
1938 : else
1939 : {
1940 0 : if (dfAngleEndForApprox > dfAngleStartForApprox &&
1941 0 : dfAngleEndForApprox < dfAngleStartForApprox + 180)
1942 : {
1943 0 : dfAngleEndForApprox -= 360;
1944 : }
1945 0 : else if (dfAngleEndForApprox < dfAngleStartForApprox &&
1946 0 : dfAngleEndForApprox > dfAngleStartForApprox - 180)
1947 : {
1948 0 : dfAngleEndForApprox += 360;
1949 : }
1950 : }
1951 : OGRLineString *poLine =
1952 : OGRGeometryFactory::approximateArcAngles(
1953 13 : pasCurves[i].u.EllipseByCenter.dfX,
1954 13 : pasCurves[i].u.EllipseByCenter.dfY,
1955 2 : padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1956 13 : pasCurves[i].u.EllipseByCenter.dfSemiMajor, dfSemiMinor,
1957 : dfRotationDeg, dfAngleStartForApprox, dfAngleEndForApprox,
1958 : 0)
1959 13 : ->toLineString();
1960 13 : if (poLine->getNumPoints() >= 2)
1961 : {
1962 15 : poLine->setPoint(
1963 13 : 0, padfX[nStartPointIdx], padfY[nStartPointIdx],
1964 2 : padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1965 2 : padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1966 15 : poLine->setPoint(
1967 13 : poLine->getNumPoints() - 1, padfX[nStartPointIdx + 1],
1968 13 : padfY[nStartPointIdx + 1],
1969 2 : padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1970 2 : padfM != nullptr ? padfM[nStartPointIdx + 1] : 0.0);
1971 : }
1972 13 : poLine->set3D(padfZ != nullptr);
1973 13 : poLine->setMeasured(padfM != nullptr);
1974 13 : poCC->addCurveDirectly(poLine);
1975 : }
1976 :
1977 : // Should not happen normally.
1978 0 : else if (nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1979 : {
1980 0 : OGRLineString *poLine = new OGRLineString();
1981 0 : poLine->setPoints(
1982 0 : 2, padfX + nStartPointIdx, padfY + nStartPointIdx,
1983 0 : padfZ != nullptr ? padfZ + nStartPointIdx : nullptr,
1984 0 : padfM != nullptr ? padfM + nStartPointIdx : nullptr);
1985 0 : poCC->addCurveDirectly(poLine);
1986 : }
1987 174 : nLastPointIdx = nStartPointIdx + 1;
1988 : }
1989 122 : if (i == nCurves)
1990 : {
1991 96 : if (pnLastCurveIdx)
1992 17 : *pnLastCurveIdx = i;
1993 : }
1994 :
1995 : // Add terminating linear segments
1996 122 : if (nLastPointIdx < nPartStartIdx + nPartPoints - 1)
1997 : {
1998 49 : OGRLineString *poLine = new OGRLineString();
1999 59 : poLine->setPoints(nPartStartIdx + nPartPoints - 1 - nLastPointIdx + 1,
2000 49 : padfX + nLastPointIdx, padfY + nLastPointIdx,
2001 14 : padfZ != nullptr ? padfZ + nLastPointIdx : nullptr,
2002 10 : padfM != nullptr ? padfM + nLastPointIdx : nullptr);
2003 49 : if (poCC->addCurveDirectly(poLine) != OGRERR_NONE)
2004 : {
2005 0 : delete poLine;
2006 0 : return nullptr;
2007 : }
2008 : }
2009 :
2010 122 : if (!bHasCircularArcs)
2011 : {
2012 29 : OGRwkbGeometryType eLSType = wkbLineString;
2013 29 : if (OGR_GT_HasZ(poCC->getGeometryType()))
2014 7 : eLSType = OGR_GT_SetZ(eLSType);
2015 29 : if (OGR_GT_HasM(poCC->getGeometryType()))
2016 7 : eLSType = OGR_GT_SetM(eLSType);
2017 29 : return reinterpret_cast<OGRCurve *>(OGR_G_ForceTo(
2018 58 : OGRGeometry::ToHandle(poCC.release()), eLSType, nullptr));
2019 : }
2020 : else
2021 93 : return poCC.release();
2022 : }
2023 :
2024 : /************************************************************************/
2025 : /* OGRCreateFromShapeBin() */
2026 : /* */
2027 : /* Translate shapefile binary representation to an OGR */
2028 : /* geometry. */
2029 : /************************************************************************/
2030 :
2031 1767 : OGRErr OGRCreateFromShapeBin(GByte *pabyShape, OGRGeometry **ppoGeom,
2032 : int nBytes)
2033 :
2034 : {
2035 1767 : *ppoGeom = nullptr;
2036 :
2037 1767 : if (nBytes < 4)
2038 : {
2039 0 : CPLError(CE_Failure, CPLE_AppDefined,
2040 : "Shape buffer size (%d) too small", nBytes);
2041 0 : return OGRERR_FAILURE;
2042 : }
2043 :
2044 : /* -------------------------------------------------------------------- */
2045 : /* Detect zlib compressed shapes and uncompress buffer if necessary */
2046 : /* NOTE: this seems to be an undocumented feature, even in the */
2047 : /* extended_shapefile_format.pdf found in the FileGDB API */
2048 : /* documentation. */
2049 : /* -------------------------------------------------------------------- */
2050 1767 : if (nBytes >= 14 && pabyShape[12] == 0x78 &&
2051 0 : pabyShape[13] == 0xDA /* zlib marker */)
2052 : {
2053 0 : GInt32 nUncompressedSize = 0;
2054 0 : GInt32 nCompressedSize = 0;
2055 0 : memcpy(&nUncompressedSize, pabyShape + 4, 4);
2056 0 : memcpy(&nCompressedSize, pabyShape + 8, 4);
2057 0 : CPL_LSBPTR32(&nUncompressedSize);
2058 0 : CPL_LSBPTR32(&nCompressedSize);
2059 0 : if (nCompressedSize + 12 == nBytes && nUncompressedSize > 0)
2060 : {
2061 : GByte *pabyUncompressedBuffer =
2062 0 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(nUncompressedSize));
2063 0 : if (pabyUncompressedBuffer == nullptr)
2064 : {
2065 0 : return OGRERR_FAILURE;
2066 : }
2067 :
2068 0 : size_t nRealUncompressedSize = 0;
2069 0 : if (CPLZLibInflate(pabyShape + 12, nCompressedSize,
2070 : pabyUncompressedBuffer, nUncompressedSize,
2071 0 : &nRealUncompressedSize) == nullptr)
2072 : {
2073 0 : CPLError(CE_Failure, CPLE_AppDefined,
2074 : "CPLZLibInflate() failed");
2075 0 : VSIFree(pabyUncompressedBuffer);
2076 0 : return OGRERR_FAILURE;
2077 : }
2078 :
2079 : const OGRErr eErr =
2080 0 : OGRCreateFromShapeBin(pabyUncompressedBuffer, ppoGeom,
2081 : static_cast<int>(nRealUncompressedSize));
2082 :
2083 0 : VSIFree(pabyUncompressedBuffer);
2084 :
2085 0 : return eErr;
2086 : }
2087 : }
2088 :
2089 1767 : int nSHPType = pabyShape[0];
2090 :
2091 : /* -------------------------------------------------------------------- */
2092 : /* Return a NULL geometry when SHPT_NULL is encountered. */
2093 : /* Watch out, null return does not mean "bad data" it means */
2094 : /* "no geometry here". Watch the OGRErr for the error status */
2095 : /* -------------------------------------------------------------------- */
2096 1767 : if (nSHPType == SHPT_NULL)
2097 : {
2098 0 : *ppoGeom = nullptr;
2099 0 : return OGRERR_NONE;
2100 : }
2101 :
2102 : #if DEBUG_VERBOSE
2103 : CPLDebug("PGeo", "Shape type read from PGeo data is nSHPType = %d",
2104 : nSHPType);
2105 : #endif
2106 :
2107 1767 : const bool bIsExtended =
2108 1767 : nSHPType >= SHPT_GENERALPOLYLINE && nSHPType <= SHPT_GENERALMULTIPATCH;
2109 :
2110 1767 : const bool bHasZ =
2111 1649 : (nSHPType == SHPT_POINTZ || nSHPType == SHPT_POINTZM ||
2112 1531 : nSHPType == SHPT_MULTIPOINTZ || nSHPType == SHPT_MULTIPOINTZM ||
2113 1295 : nSHPType == SHPT_POLYGONZ || nSHPType == SHPT_POLYGONZM ||
2114 1055 : nSHPType == SHPT_ARCZ || nSHPType == SHPT_ARCZM ||
2115 3513 : nSHPType == SHPT_MULTIPATCH || nSHPType == SHPT_MULTIPATCHM ||
2116 97 : (bIsExtended && (pabyShape[3] & 0x80) != 0));
2117 :
2118 1767 : const bool bHasM =
2119 1767 : (nSHPType == SHPT_POINTM || nSHPType == SHPT_POINTZM ||
2120 1767 : nSHPType == SHPT_MULTIPOINTM || nSHPType == SHPT_MULTIPOINTZM ||
2121 1767 : nSHPType == SHPT_POLYGONM || nSHPType == SHPT_POLYGONZM ||
2122 1763 : nSHPType == SHPT_ARCM || nSHPType == SHPT_ARCZM ||
2123 3631 : nSHPType == SHPT_MULTIPATCHM ||
2124 97 : (bIsExtended && (pabyShape[3] & 0x40) != 0));
2125 :
2126 1767 : const bool bHasCurves = (bIsExtended && (pabyShape[3] & 0x20) != 0);
2127 :
2128 1767 : switch (nSHPType)
2129 : {
2130 44 : case SHPT_GENERALPOLYLINE:
2131 44 : nSHPType = SHPT_ARC;
2132 44 : break;
2133 53 : case SHPT_GENERALPOLYGON:
2134 53 : nSHPType = SHPT_POLYGON;
2135 53 : break;
2136 0 : case SHPT_GENERALPOINT:
2137 0 : nSHPType = SHPT_POINT;
2138 0 : break;
2139 0 : case SHPT_GENERALMULTIPOINT:
2140 0 : nSHPType = SHPT_MULTIPOINT;
2141 0 : break;
2142 0 : case SHPT_GENERALMULTIPATCH:
2143 0 : nSHPType = SHPT_MULTIPATCH;
2144 : }
2145 :
2146 : /* ==================================================================== */
2147 : /* Extract vertices for a Polygon or Arc. */
2148 : /* ==================================================================== */
2149 1767 : if (nSHPType == SHPT_ARC || nSHPType == SHPT_ARCZ ||
2150 1248 : nSHPType == SHPT_ARCM || nSHPType == SHPT_ARCZM ||
2151 954 : nSHPType == SHPT_POLYGON || nSHPType == SHPT_POLYGONZ ||
2152 718 : nSHPType == SHPT_POLYGONM || nSHPType == SHPT_POLYGONZM ||
2153 478 : nSHPType == SHPT_MULTIPATCH || nSHPType == SHPT_MULTIPATCHM)
2154 : {
2155 1289 : if (nBytes < 44)
2156 : {
2157 0 : CPLError(CE_Failure, CPLE_AppDefined,
2158 : "Corrupted Shape : nBytes=%d, nSHPType=%d", nBytes,
2159 : nSHPType);
2160 0 : return OGRERR_FAILURE;
2161 : }
2162 :
2163 : /* --------------------------------------------------------------------
2164 : */
2165 : /* Extract part/point count, and build vertex and part arrays */
2166 : /* to proper size. */
2167 : /* --------------------------------------------------------------------
2168 : */
2169 1289 : GInt32 nPoints = 0;
2170 1289 : memcpy(&nPoints, pabyShape + 40, 4);
2171 1289 : GInt32 nParts = 0;
2172 1289 : memcpy(&nParts, pabyShape + 36, 4);
2173 :
2174 1289 : CPL_LSBPTR32(&nPoints);
2175 1289 : CPL_LSBPTR32(&nParts);
2176 :
2177 1289 : if (nPoints < 0 || nParts < 0 || nPoints > 50 * 1000 * 1000 ||
2178 1289 : nParts > 10 * 1000 * 1000)
2179 : {
2180 0 : CPLError(CE_Failure, CPLE_AppDefined,
2181 : "Corrupted Shape : nPoints=%d, nParts=%d.", nPoints,
2182 : nParts);
2183 0 : return OGRERR_FAILURE;
2184 : }
2185 :
2186 1289 : const bool bIsMultiPatch =
2187 1289 : nSHPType == SHPT_MULTIPATCH || nSHPType == SHPT_MULTIPATCHM;
2188 :
2189 : // With the previous checks on nPoints and nParts,
2190 : // we should not overflow here and after
2191 : // since 50 M * (16 + 8 + 8) = 1 600 MB.
2192 1289 : int nRequiredSize = 44 + 4 * nParts + 16 * nPoints;
2193 1289 : if (bHasZ)
2194 : {
2195 735 : nRequiredSize += 16 + 8 * nPoints;
2196 : }
2197 1289 : if (bHasM)
2198 : {
2199 25 : nRequiredSize += 16 + 8 * nPoints;
2200 : }
2201 1289 : if (bIsMultiPatch)
2202 : {
2203 236 : nRequiredSize += 4 * nParts;
2204 : }
2205 1289 : if (nRequiredSize > nBytes)
2206 : {
2207 0 : CPLError(CE_Failure, CPLE_AppDefined,
2208 : "Corrupted Shape : nPoints=%d, nParts=%d, nBytes=%d, "
2209 : "nSHPType=%d, nRequiredSize=%d",
2210 : nPoints, nParts, nBytes, nSHPType, nRequiredSize);
2211 0 : return OGRERR_FAILURE;
2212 : }
2213 :
2214 : GInt32 *panPartStart =
2215 1289 : static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(nParts, sizeof(GInt32)));
2216 1289 : if (nParts != 0 && panPartStart == nullptr)
2217 : {
2218 0 : return OGRERR_FAILURE;
2219 : }
2220 :
2221 : /* --------------------------------------------------------------------
2222 : */
2223 : /* Copy out the part array from the record. */
2224 : /* --------------------------------------------------------------------
2225 : */
2226 1289 : memcpy(panPartStart, pabyShape + 44, 4 * nParts);
2227 3446 : for (int i = 0; i < nParts; i++)
2228 : {
2229 2157 : CPL_LSBPTR32(panPartStart + i);
2230 :
2231 : // Check that the offset is inside the vertex array.
2232 2157 : if (panPartStart[i] < 0 || panPartStart[i] >= nPoints)
2233 : {
2234 0 : CPLError(
2235 : CE_Failure, CPLE_AppDefined,
2236 : "Corrupted Shape : panPartStart[%d] = %d, nPoints = %d", i,
2237 0 : panPartStart[i], nPoints);
2238 0 : CPLFree(panPartStart);
2239 0 : return OGRERR_FAILURE;
2240 : }
2241 2157 : if (i > 0 && panPartStart[i] <= panPartStart[i - 1])
2242 : {
2243 0 : CPLError(CE_Failure, CPLE_AppDefined,
2244 : "Corrupted Shape : panPartStart[%d] = %d, "
2245 : "panPartStart[%d] = %d",
2246 0 : i, panPartStart[i], i - 1, panPartStart[i - 1]);
2247 0 : CPLFree(panPartStart);
2248 0 : return OGRERR_FAILURE;
2249 : }
2250 : }
2251 :
2252 1289 : int nOffset = 44 + 4 * nParts;
2253 :
2254 : /* --------------------------------------------------------------------
2255 : */
2256 : /* If this is a multipatch, we will also have parts types. */
2257 : /* --------------------------------------------------------------------
2258 : */
2259 1289 : GInt32 *panPartType = nullptr;
2260 :
2261 1289 : if (bIsMultiPatch)
2262 : {
2263 : panPartType = static_cast<GInt32 *>(
2264 236 : VSI_MALLOC2_VERBOSE(nParts, sizeof(GInt32)));
2265 236 : if (panPartType == nullptr)
2266 : {
2267 0 : CPLFree(panPartStart);
2268 0 : return OGRERR_FAILURE;
2269 : }
2270 :
2271 236 : memcpy(panPartType, pabyShape + nOffset, 4 * nParts);
2272 944 : for (int i = 0; i < nParts; i++)
2273 : {
2274 708 : CPL_LSBPTR32(panPartType + i);
2275 : }
2276 236 : nOffset += 4 * nParts;
2277 : }
2278 :
2279 : /* --------------------------------------------------------------------
2280 : */
2281 : /* Copy out the vertices from the record. */
2282 : /* --------------------------------------------------------------------
2283 : */
2284 : double *padfX =
2285 1289 : static_cast<double *>(VSI_MALLOC_VERBOSE(sizeof(double) * nPoints));
2286 : double *padfY =
2287 1289 : static_cast<double *>(VSI_MALLOC_VERBOSE(sizeof(double) * nPoints));
2288 : double *padfZ =
2289 1289 : static_cast<double *>(VSI_CALLOC_VERBOSE(sizeof(double), nPoints));
2290 1289 : double *padfM = static_cast<double *>(
2291 25 : bHasM ? VSI_CALLOC_VERBOSE(sizeof(double), nPoints) : nullptr);
2292 :
2293 1289 : if (nPoints != 0 && (padfX == nullptr || padfY == nullptr ||
2294 1288 : padfZ == nullptr || (bHasM && padfM == nullptr)))
2295 : {
2296 0 : CPLFree(panPartStart);
2297 0 : CPLFree(panPartType);
2298 0 : CPLFree(padfX);
2299 0 : CPLFree(padfY);
2300 0 : CPLFree(padfZ);
2301 0 : CPLFree(padfM);
2302 0 : return OGRERR_FAILURE;
2303 : }
2304 :
2305 10974 : for (int i = 0; i < nPoints; i++)
2306 : {
2307 9685 : memcpy(padfX + i, pabyShape + nOffset + i * 16, 8);
2308 9685 : memcpy(padfY + i, pabyShape + nOffset + i * 16 + 8, 8);
2309 9685 : CPL_LSBPTR64(padfX + i);
2310 9685 : CPL_LSBPTR64(padfY + i);
2311 : }
2312 :
2313 1289 : nOffset += 16 * nPoints;
2314 :
2315 : /* --------------------------------------------------------------------
2316 : */
2317 : /* If we have a Z coordinate, collect that now. */
2318 : /* --------------------------------------------------------------------
2319 : */
2320 1289 : if (bHasZ)
2321 : {
2322 5477 : for (int i = 0; i < nPoints; i++)
2323 : {
2324 4742 : memcpy(padfZ + i, pabyShape + nOffset + 16 + i * 8, 8);
2325 4742 : CPL_LSBPTR64(padfZ + i);
2326 : }
2327 :
2328 735 : nOffset += 16 + 8 * nPoints;
2329 : }
2330 :
2331 : /* --------------------------------------------------------------------
2332 : */
2333 : /* If we have a M coordinate, collect that now. */
2334 : /* --------------------------------------------------------------------
2335 : */
2336 1289 : if (bHasM)
2337 : {
2338 25 : bool bIsAllNAN = nPoints > 0;
2339 155 : for (int i = 0; i < nPoints; i++)
2340 : {
2341 130 : memcpy(padfM + i, pabyShape + nOffset + 16 + i * 8, 8);
2342 130 : CPL_LSBPTR64(padfM + i);
2343 130 : bIsAllNAN &= std::isnan(padfM[i]);
2344 : }
2345 25 : if (bIsAllNAN)
2346 : {
2347 4 : CPLFree(padfM);
2348 4 : padfM = nullptr;
2349 : }
2350 :
2351 25 : nOffset += 16 + 8 * nPoints;
2352 : }
2353 :
2354 : /* --------------------------------------------------------------------
2355 : */
2356 : /* If we have curves, collect them now. */
2357 : /* --------------------------------------------------------------------
2358 : */
2359 1289 : int nCurves = 0;
2360 1289 : CurveSegment *pasCurves = nullptr;
2361 1289 : if (bHasCurves && nOffset + 4 <= nBytes)
2362 : {
2363 97 : memcpy(&nCurves, pabyShape + nOffset, 4);
2364 97 : CPL_LSBPTR32(&nCurves);
2365 97 : nOffset += 4;
2366 : #ifdef DEBUG_VERBOSE
2367 : CPLDebug("OGR", "nCurves = %d", nCurves);
2368 : #endif
2369 97 : if (nCurves < 0 || nCurves > (nBytes - nOffset) / (8 + 20))
2370 : {
2371 0 : CPLDebug("OGR", "Invalid nCurves = %d", nCurves);
2372 0 : nCurves = 0;
2373 : }
2374 : pasCurves = static_cast<CurveSegment *>(
2375 97 : VSI_MALLOC2_VERBOSE(sizeof(CurveSegment), nCurves));
2376 97 : if (pasCurves == nullptr)
2377 : {
2378 0 : nCurves = 0;
2379 : }
2380 97 : int iCurve = 0;
2381 272 : for (int i = 0; i < nCurves; i++)
2382 : {
2383 175 : if (nOffset + 8 > nBytes)
2384 : {
2385 0 : CPLDebug("OGR", "Not enough bytes");
2386 0 : break;
2387 : }
2388 175 : int nStartPointIdx = 0;
2389 175 : memcpy(&nStartPointIdx, pabyShape + nOffset, 4);
2390 175 : CPL_LSBPTR32(&nStartPointIdx);
2391 175 : nOffset += 4;
2392 175 : int nSegmentType = 0;
2393 175 : memcpy(&nSegmentType, pabyShape + nOffset, 4);
2394 175 : CPL_LSBPTR32(&nSegmentType);
2395 175 : nOffset += 4;
2396 : #ifdef DEBUG_VERBOSE
2397 : CPLDebug("OGR", "[%d] nStartPointIdx = %d, segmentType = %d", i,
2398 : nSegmentType, nSegmentType);
2399 : #endif
2400 175 : if (nStartPointIdx < 0 || nStartPointIdx >= nPoints ||
2401 78 : (iCurve > 0 &&
2402 78 : nStartPointIdx <= pasCurves[iCurve - 1].nStartPointIdx))
2403 : {
2404 0 : CPLDebug("OGR", "Invalid nStartPointIdx = %d",
2405 : nStartPointIdx);
2406 0 : break;
2407 : }
2408 175 : pasCurves[iCurve].nStartPointIdx = nStartPointIdx;
2409 175 : if (nSegmentType == EXT_SHAPE_SEGMENT_ARC)
2410 : {
2411 126 : if (nOffset + 20 > nBytes)
2412 : {
2413 0 : CPLDebug("OGR", "Not enough bytes");
2414 0 : break;
2415 : }
2416 126 : double dfVal1 = 0.0;
2417 126 : double dfVal2 = 0.0;
2418 126 : memcpy(&dfVal1, pabyShape + nOffset + 0, 8);
2419 126 : CPL_LSBPTR64(&dfVal1);
2420 126 : memcpy(&dfVal2, pabyShape + nOffset + 8, 8);
2421 126 : CPL_LSBPTR64(&dfVal2);
2422 126 : int nBits = 0;
2423 126 : memcpy(&nBits, pabyShape + nOffset + 16, 4);
2424 126 : CPL_LSBPTR32(&nBits);
2425 :
2426 : #ifdef DEBUG_VERBOSE
2427 : CPLDebug("OGR", "Arc: ");
2428 : CPLDebug("OGR", " dfVal1 = %f, dfVal2 = %f, nBits=%X",
2429 : dfVal1, dfVal2, nBits);
2430 : if (nBits & EXT_SHAPE_ARC_EMPTY)
2431 : CPLDebug("OGR", " IsEmpty");
2432 : if (nBits & EXT_SHAPE_ARC_CCW)
2433 : CPLDebug("OGR", " IsCCW");
2434 : if (nBits & EXT_SHAPE_ARC_MINOR)
2435 : CPLDebug("OGR", " IsMinor");
2436 : if (nBits & EXT_SHAPE_ARC_LINE)
2437 : CPLDebug("OGR", " IsLine");
2438 : if (nBits & EXT_SHAPE_ARC_POINT)
2439 : CPLDebug("OGR", " IsPoint");
2440 : if (nBits & EXT_SHAPE_ARC_IP)
2441 : CPLDebug("OGR", " DefinedIP");
2442 : #endif
2443 126 : if ((nBits & EXT_SHAPE_ARC_IP) != 0 &&
2444 114 : (nBits & EXT_SHAPE_ARC_LINE) == 0)
2445 : {
2446 113 : pasCurves[iCurve].eType = CURVE_ARC_INTERIOR_POINT;
2447 113 : pasCurves[iCurve].u.ArcByIntermediatePoint.dfX = dfVal1;
2448 113 : pasCurves[iCurve].u.ArcByIntermediatePoint.dfY = dfVal2;
2449 113 : iCurve++;
2450 : }
2451 13 : else if ((nBits & EXT_SHAPE_ARC_EMPTY) == 0 &&
2452 13 : (nBits & EXT_SHAPE_ARC_LINE) == 0 &&
2453 12 : (nBits & EXT_SHAPE_ARC_POINT) == 0)
2454 : {
2455 : // This is the old deprecated way
2456 12 : pasCurves[iCurve].eType = CURVE_ARC_CENTER_POINT;
2457 12 : pasCurves[iCurve].u.ArcByCenterPoint.dfX = dfVal1;
2458 12 : pasCurves[iCurve].u.ArcByCenterPoint.dfY = dfVal2;
2459 12 : pasCurves[iCurve].u.ArcByCenterPoint.bIsCCW =
2460 12 : (nBits & EXT_SHAPE_ARC_CCW) != 0;
2461 12 : iCurve++;
2462 : }
2463 126 : nOffset += 16 + 4;
2464 : }
2465 49 : else if (nSegmentType == EXT_SHAPE_SEGMENT_BEZIER)
2466 : {
2467 36 : if (nOffset + 32 > nBytes)
2468 : {
2469 0 : CPLDebug("OGR", "Not enough bytes");
2470 0 : break;
2471 : }
2472 36 : double dfX1 = 0.0;
2473 36 : double dfY1 = 0.0;
2474 36 : memcpy(&dfX1, pabyShape + nOffset + 0, 8);
2475 36 : CPL_LSBPTR64(&dfX1);
2476 36 : memcpy(&dfY1, pabyShape + nOffset + 8, 8);
2477 36 : CPL_LSBPTR64(&dfY1);
2478 36 : double dfX2 = 0.0;
2479 36 : double dfY2 = 0.0;
2480 36 : memcpy(&dfX2, pabyShape + nOffset + 16, 8);
2481 36 : CPL_LSBPTR64(&dfX2);
2482 36 : memcpy(&dfY2, pabyShape + nOffset + 24, 8);
2483 36 : CPL_LSBPTR64(&dfY2);
2484 : #ifdef DEBUG_VERBOSE
2485 : CPLDebug("OGR", "Bezier:");
2486 : CPLDebug("OGR", " dfX1 = %f, dfY1 = %f", dfX1, dfY1);
2487 : CPLDebug("OGR", " dfX2 = %f, dfY2 = %f", dfX2, dfY2);
2488 : #endif
2489 36 : pasCurves[iCurve].eType = CURVE_BEZIER;
2490 36 : pasCurves[iCurve].u.Bezier.dfX1 = dfX1;
2491 36 : pasCurves[iCurve].u.Bezier.dfY1 = dfY1;
2492 36 : pasCurves[iCurve].u.Bezier.dfX2 = dfX2;
2493 36 : pasCurves[iCurve].u.Bezier.dfY2 = dfY2;
2494 36 : iCurve++;
2495 36 : nOffset += 32;
2496 : }
2497 13 : else if (nSegmentType == EXT_SHAPE_SEGMENT_ELLIPSE)
2498 : {
2499 13 : if (nOffset + 44 > nBytes)
2500 : {
2501 0 : CPLDebug("OGR", "Not enough bytes");
2502 0 : break;
2503 : }
2504 13 : double dfVS0 = 0.0;
2505 13 : memcpy(&dfVS0, pabyShape + nOffset, 8);
2506 13 : nOffset += 8;
2507 13 : CPL_LSBPTR64(&dfVS0);
2508 :
2509 13 : double dfVS1 = 0.0;
2510 13 : memcpy(&dfVS1, pabyShape + nOffset, 8);
2511 13 : nOffset += 8;
2512 13 : CPL_LSBPTR64(&dfVS1);
2513 :
2514 13 : double dfRotationOrFromV = 0.0;
2515 13 : memcpy(&dfRotationOrFromV, pabyShape + nOffset, 8);
2516 13 : nOffset += 8;
2517 13 : CPL_LSBPTR64(&dfRotationOrFromV);
2518 :
2519 13 : double dfSemiMajor = 0.0;
2520 13 : memcpy(&dfSemiMajor, pabyShape + nOffset, 8);
2521 13 : nOffset += 8;
2522 13 : CPL_LSBPTR64(&dfSemiMajor);
2523 :
2524 13 : double dfMinorMajorRatioOrDeltaV = 0.0;
2525 13 : memcpy(&dfMinorMajorRatioOrDeltaV, pabyShape + nOffset, 8);
2526 13 : nOffset += 8;
2527 13 : CPL_LSBPTR64(&dfMinorMajorRatioOrDeltaV);
2528 :
2529 13 : int nBits = 0;
2530 13 : memcpy(&nBits, pabyShape + nOffset, 4);
2531 13 : CPL_LSBPTR32(&nBits);
2532 13 : nOffset += 4;
2533 :
2534 : #ifdef DEBUG_VERBOSE
2535 : CPLDebug("OGR", "Ellipse:");
2536 : CPLDebug("OGR", " dfVS0 = %f", dfVS0);
2537 : CPLDebug("OGR", " dfVS1 = %f", dfVS1);
2538 : CPLDebug("OGR", " dfRotationOrFromV = %f",
2539 : dfRotationOrFromV);
2540 : CPLDebug("OGR", " dfSemiMajor = %f", dfSemiMajor);
2541 : CPLDebug("OGR", " dfMinorMajorRatioOrDeltaV = %f",
2542 : dfMinorMajorRatioOrDeltaV);
2543 : CPLDebug("OGR", " nBits=%X", nBits);
2544 :
2545 : if (nBits & EXT_SHAPE_ELLIPSE_EMPTY)
2546 : CPLDebug("OGR", " IsEmpty");
2547 : if (nBits & EXT_SHAPE_ELLIPSE_LINE)
2548 : CPLDebug("OGR", " IsLine");
2549 : if (nBits & EXT_SHAPE_ELLIPSE_POINT)
2550 : CPLDebug("OGR", " IsPoint");
2551 : if (nBits & EXT_SHAPE_ELLIPSE_CIRCULAR)
2552 : CPLDebug("OGR", " IsCircular");
2553 : if (nBits & EXT_SHAPE_ELLIPSE_CENTER_TO)
2554 : CPLDebug("OGR", " CenterTo");
2555 : if (nBits & EXT_SHAPE_ELLIPSE_CENTER_FROM)
2556 : CPLDebug("OGR", " CenterFrom");
2557 : if (nBits & EXT_SHAPE_ELLIPSE_CCW)
2558 : CPLDebug("OGR", " IsCCW");
2559 : if (nBits & EXT_SHAPE_ELLIPSE_MINOR)
2560 : CPLDebug("OGR", " IsMinor");
2561 : if (nBits & EXT_SHAPE_ELLIPSE_COMPLETE)
2562 : CPLDebug("OGR", " IsComplete");
2563 : #endif
2564 :
2565 13 : if ((nBits & EXT_SHAPE_ELLIPSE_CENTER_TO) == 0 &&
2566 13 : (nBits & EXT_SHAPE_ELLIPSE_CENTER_FROM) == 0)
2567 : {
2568 13 : pasCurves[iCurve].eType = CURVE_ELLIPSE_BY_CENTER;
2569 13 : pasCurves[iCurve].u.EllipseByCenter.dfX = dfVS0;
2570 13 : pasCurves[iCurve].u.EllipseByCenter.dfY = dfVS1;
2571 13 : pasCurves[iCurve].u.EllipseByCenter.dfRotationDeg =
2572 13 : dfRotationOrFromV / M_PI * 180;
2573 13 : pasCurves[iCurve].u.EllipseByCenter.dfSemiMajor =
2574 : dfSemiMajor;
2575 13 : pasCurves[iCurve].u.EllipseByCenter.dfRatioSemiMinor =
2576 : dfMinorMajorRatioOrDeltaV;
2577 13 : pasCurves[iCurve].u.EllipseByCenter.bIsMinor =
2578 13 : ((nBits & EXT_SHAPE_ELLIPSE_MINOR) != 0);
2579 13 : pasCurves[iCurve].u.EllipseByCenter.bIsComplete =
2580 13 : ((nBits & EXT_SHAPE_ELLIPSE_COMPLETE) != 0);
2581 13 : iCurve++;
2582 : }
2583 : }
2584 : else
2585 : {
2586 0 : CPLDebug("OGR", "unhandled segmentType = %d", nSegmentType);
2587 : }
2588 : }
2589 :
2590 97 : nCurves = iCurve;
2591 : }
2592 :
2593 : /* --------------------------------------------------------------------
2594 : */
2595 : /* Build corresponding OGR objects. */
2596 : /* --------------------------------------------------------------------
2597 : */
2598 1289 : if (nSHPType == SHPT_ARC || nSHPType == SHPT_ARCZ ||
2599 770 : nSHPType == SHPT_ARCM || nSHPType == SHPT_ARCZM)
2600 : {
2601 : /* --------------------------------------------------------------------
2602 : */
2603 : /* Arc - As LineString */
2604 : /* --------------------------------------------------------------------
2605 : */
2606 519 : if (nParts == 1)
2607 : {
2608 396 : if (nCurves > 0)
2609 : {
2610 38 : *ppoGeom = OGRShapeCreateCompoundCurve(
2611 : 0, nPoints, pasCurves, nCurves, 0, padfX, padfY,
2612 : bHasZ ? padfZ : nullptr, padfM, nullptr);
2613 : }
2614 : else
2615 : {
2616 358 : OGRLineString *poLine = new OGRLineString();
2617 358 : *ppoGeom = poLine;
2618 :
2619 358 : poLine->setPoints(nPoints, padfX, padfY, padfZ, padfM);
2620 : }
2621 : }
2622 :
2623 : /* --------------------------------------------------------------------
2624 : */
2625 : /* Arc - As MultiLineString */
2626 : /* --------------------------------------------------------------------
2627 : */
2628 : else
2629 : {
2630 123 : if (nCurves > 0)
2631 : {
2632 5 : OGRMultiCurve *poMulti = new OGRMultiCurve;
2633 5 : *ppoGeom = poMulti;
2634 :
2635 5 : int iCurveIdx = 0;
2636 18 : for (int i = 0; i < nParts; i++)
2637 : {
2638 13 : const int nVerticesInThisPart =
2639 13 : i == nParts - 1
2640 13 : ? nPoints - panPartStart[i]
2641 8 : : panPartStart[i + 1] - panPartStart[i];
2642 :
2643 13 : OGRCurve *poCurve = OGRShapeCreateCompoundCurve(
2644 13 : panPartStart[i], nVerticesInThisPart, pasCurves,
2645 : nCurves, iCurveIdx, padfX, padfY,
2646 : bHasZ ? padfZ : nullptr, padfM, &iCurveIdx);
2647 26 : if (poCurve == nullptr || poMulti->addGeometryDirectly(
2648 13 : poCurve) != OGRERR_NONE)
2649 : {
2650 0 : delete poCurve;
2651 0 : delete poMulti;
2652 0 : *ppoGeom = nullptr;
2653 0 : break;
2654 : }
2655 : }
2656 : }
2657 : else
2658 : {
2659 118 : OGRMultiLineString *poMulti = new OGRMultiLineString;
2660 118 : *ppoGeom = poMulti;
2661 :
2662 354 : for (int i = 0; i < nParts; i++)
2663 : {
2664 236 : OGRLineString *poLine = new OGRLineString;
2665 236 : const int nVerticesInThisPart =
2666 236 : i == nParts - 1
2667 236 : ? nPoints - panPartStart[i]
2668 118 : : panPartStart[i + 1] - panPartStart[i];
2669 :
2670 236 : poLine->setPoints(
2671 236 : nVerticesInThisPart, padfX + panPartStart[i],
2672 236 : padfY + panPartStart[i], padfZ + panPartStart[i],
2673 0 : padfM != nullptr ? padfM + panPartStart[i]
2674 : : nullptr);
2675 :
2676 236 : poMulti->addGeometryDirectly(poLine);
2677 : }
2678 : }
2679 519 : }
2680 : } // ARC.
2681 :
2682 : /* --------------------------------------------------------------------
2683 : */
2684 : /* Polygon */
2685 : /* --------------------------------------------------------------------
2686 : */
2687 770 : else if (nSHPType == SHPT_POLYGON || nSHPType == SHPT_POLYGONZ ||
2688 240 : nSHPType == SHPT_POLYGONM || nSHPType == SHPT_POLYGONZM)
2689 : {
2690 534 : if (nCurves > 0 && nParts != 0)
2691 : {
2692 53 : if (nParts == 1)
2693 : {
2694 41 : OGRCurvePolygon *poOGRPoly = new OGRCurvePolygon;
2695 41 : *ppoGeom = poOGRPoly;
2696 41 : const int nVerticesInThisPart = nPoints - panPartStart[0];
2697 :
2698 41 : OGRCurve *poRing = OGRShapeCreateCompoundCurve(
2699 : panPartStart[0], nVerticesInThisPart, pasCurves,
2700 : nCurves, 0, padfX, padfY, bHasZ ? padfZ : nullptr,
2701 : padfM, nullptr);
2702 82 : if (poRing == nullptr ||
2703 41 : poOGRPoly->addRingDirectly(poRing) != OGRERR_NONE)
2704 : {
2705 0 : delete poRing;
2706 0 : delete poOGRPoly;
2707 0 : *ppoGeom = nullptr;
2708 : }
2709 : }
2710 : else
2711 : {
2712 12 : OGRGeometry *poOGR = nullptr;
2713 : OGRCurvePolygon **tabPolygons =
2714 12 : new OGRCurvePolygon *[nParts];
2715 :
2716 12 : int iCurveIdx = 0;
2717 42 : for (int i = 0; i < nParts; i++)
2718 : {
2719 30 : tabPolygons[i] = new OGRCurvePolygon();
2720 30 : const int nVerticesInThisPart =
2721 30 : i == nParts - 1
2722 30 : ? nPoints - panPartStart[i]
2723 18 : : panPartStart[i + 1] - panPartStart[i];
2724 :
2725 30 : OGRCurve *poRing = OGRShapeCreateCompoundCurve(
2726 30 : panPartStart[i], nVerticesInThisPart, pasCurves,
2727 : nCurves, iCurveIdx, padfX, padfY,
2728 : bHasZ ? padfZ : nullptr, padfM, &iCurveIdx);
2729 60 : if (poRing == nullptr ||
2730 30 : tabPolygons[i]->addRingDirectly(poRing) !=
2731 : OGRERR_NONE)
2732 : {
2733 0 : delete poRing;
2734 0 : for (; i >= 0; --i)
2735 0 : delete tabPolygons[i];
2736 0 : delete[] tabPolygons;
2737 0 : tabPolygons = nullptr;
2738 0 : *ppoGeom = nullptr;
2739 0 : break;
2740 : }
2741 : }
2742 :
2743 12 : if (tabPolygons != nullptr)
2744 : {
2745 12 : int isValidGeometry = FALSE;
2746 12 : const char *papszOptions[] = {"METHOD=ONLY_CCW",
2747 : nullptr};
2748 12 : poOGR = OGRGeometryFactory::organizePolygons(
2749 : reinterpret_cast<OGRGeometry **>(tabPolygons),
2750 : nParts, &isValidGeometry, papszOptions);
2751 :
2752 12 : if (!isValidGeometry)
2753 : {
2754 0 : CPLError(CE_Warning, CPLE_AppDefined,
2755 : "Geometry of polygon cannot be translated "
2756 : "to Simple Geometry. All polygons will "
2757 : "be contained in a multipolygon.");
2758 : }
2759 :
2760 12 : *ppoGeom = poOGR;
2761 12 : delete[] tabPolygons;
2762 : }
2763 53 : }
2764 : }
2765 481 : else if (nParts != 0)
2766 : {
2767 480 : if (nParts == 1)
2768 : {
2769 360 : OGRPolygon *poOGRPoly = new OGRPolygon;
2770 360 : *ppoGeom = poOGRPoly;
2771 360 : OGRLinearRing *poRing = new OGRLinearRing;
2772 360 : int nVerticesInThisPart = nPoints - panPartStart[0];
2773 :
2774 360 : poRing->setPoints(
2775 360 : nVerticesInThisPart, padfX + panPartStart[0],
2776 360 : padfY + panPartStart[0], padfZ + panPartStart[0],
2777 2 : padfM != nullptr ? padfM + panPartStart[0] : nullptr);
2778 :
2779 360 : if (poOGRPoly->addRingDirectly(poRing) != OGRERR_NONE)
2780 : {
2781 0 : delete poRing;
2782 0 : delete poOGRPoly;
2783 0 : *ppoGeom = nullptr;
2784 : }
2785 : }
2786 : else
2787 : {
2788 120 : OGRGeometry *poOGR = nullptr;
2789 120 : OGRPolygon **tabPolygons = new OGRPolygon *[nParts];
2790 :
2791 493 : for (int i = 0; i < nParts; i++)
2792 : {
2793 373 : tabPolygons[i] = new OGRPolygon();
2794 373 : OGRLinearRing *poRing = new OGRLinearRing;
2795 373 : const int nVerticesInThisPart =
2796 373 : i == nParts - 1
2797 373 : ? nPoints - panPartStart[i]
2798 253 : : panPartStart[i + 1] - panPartStart[i];
2799 :
2800 373 : poRing->setPoints(
2801 373 : nVerticesInThisPart, padfX + panPartStart[i],
2802 373 : padfY + panPartStart[i], padfZ + panPartStart[i],
2803 0 : padfM != nullptr ? padfM + panPartStart[i]
2804 : : nullptr);
2805 373 : if (tabPolygons[i]->addRingDirectly(poRing) !=
2806 : OGRERR_NONE)
2807 : {
2808 0 : delete poRing;
2809 0 : for (; i >= 0; --i)
2810 0 : delete tabPolygons[i];
2811 0 : delete[] tabPolygons;
2812 0 : tabPolygons = nullptr;
2813 0 : *ppoGeom = nullptr;
2814 0 : break;
2815 : }
2816 : }
2817 :
2818 120 : if (tabPolygons != nullptr)
2819 : {
2820 120 : int isValidGeometry = FALSE;
2821 : // The outer ring is supposed to be clockwise oriented
2822 : // If it is not, then use the default/slow method.
2823 120 : const char *papszOptions[] = {
2824 120 : !(tabPolygons[0]->getExteriorRing()->isClockwise())
2825 120 : ? "METHOD=DEFAULT"
2826 : : "METHOD=ONLY_CCW",
2827 120 : nullptr};
2828 120 : poOGR = OGRGeometryFactory::organizePolygons(
2829 : reinterpret_cast<OGRGeometry **>(tabPolygons),
2830 : nParts, &isValidGeometry, papszOptions);
2831 :
2832 120 : if (!isValidGeometry)
2833 : {
2834 0 : CPLError(CE_Warning, CPLE_AppDefined,
2835 : "Geometry of polygon cannot be translated "
2836 : "to Simple Geometry. All polygons will be "
2837 : "contained in a multipolygon.");
2838 : }
2839 :
2840 120 : *ppoGeom = poOGR;
2841 120 : delete[] tabPolygons;
2842 : }
2843 : }
2844 : }
2845 : else
2846 : {
2847 1 : *ppoGeom = new OGRPolygon();
2848 534 : }
2849 : } // Polygon.
2850 :
2851 : /* --------------------------------------------------------------------
2852 : */
2853 : /* Multipatch */
2854 : /* --------------------------------------------------------------------
2855 : */
2856 236 : else if (bIsMultiPatch)
2857 : {
2858 236 : *ppoGeom =
2859 236 : OGRCreateFromMultiPatch(nParts, panPartStart, panPartType,
2860 : nPoints, padfX, padfY, padfZ);
2861 : }
2862 :
2863 1289 : CPLFree(panPartStart);
2864 1289 : CPLFree(panPartType);
2865 1289 : CPLFree(padfX);
2866 1289 : CPLFree(padfY);
2867 1289 : CPLFree(padfZ);
2868 1289 : CPLFree(padfM);
2869 1289 : CPLFree(pasCurves);
2870 :
2871 1289 : if (*ppoGeom != nullptr)
2872 : {
2873 1289 : if (!bHasZ)
2874 554 : (*ppoGeom)->set3D(FALSE);
2875 : }
2876 :
2877 1289 : return *ppoGeom != nullptr ? OGRERR_NONE : OGRERR_FAILURE;
2878 : }
2879 :
2880 : /* ==================================================================== */
2881 : /* Extract vertices for a MultiPoint. */
2882 : /* ==================================================================== */
2883 478 : else if (nSHPType == SHPT_MULTIPOINT || nSHPType == SHPT_MULTIPOINTM ||
2884 242 : nSHPType == SHPT_MULTIPOINTZ || nSHPType == SHPT_MULTIPOINTZM)
2885 : {
2886 236 : GInt32 nPoints = 0;
2887 236 : memcpy(&nPoints, pabyShape + 36, 4);
2888 236 : CPL_LSBPTR32(&nPoints);
2889 :
2890 236 : if (nPoints < 0 || nPoints > 50 * 1000 * 1000)
2891 : {
2892 0 : CPLError(CE_Failure, CPLE_AppDefined,
2893 : "Corrupted Shape : nPoints=%d.", nPoints);
2894 0 : return OGRERR_FAILURE;
2895 : }
2896 :
2897 236 : const GInt32 nOffsetZ = 40 + 2 * 8 * nPoints + 2 * 8;
2898 236 : GInt32 nOffsetM = 0;
2899 236 : if (bHasM)
2900 0 : nOffsetM = bHasZ ? nOffsetZ + 2 * 8 * 8 * nPoints : nOffsetZ;
2901 :
2902 236 : OGRMultiPoint *poMultiPt = new OGRMultiPoint;
2903 236 : *ppoGeom = poMultiPt;
2904 :
2905 708 : for (int i = 0; i < nPoints; i++)
2906 : {
2907 472 : OGRPoint *poPt = new OGRPoint;
2908 :
2909 : // Copy X.
2910 472 : double x = 0.0;
2911 472 : memcpy(&x, pabyShape + 40 + i * 16, 8);
2912 472 : CPL_LSBPTR64(&x);
2913 472 : poPt->setX(x);
2914 :
2915 : // Copy Y.
2916 472 : double y = 0.0;
2917 472 : memcpy(&y, pabyShape + 40 + i * 16 + 8, 8);
2918 472 : CPL_LSBPTR64(&y);
2919 472 : poPt->setY(y);
2920 :
2921 : // Copy Z.
2922 472 : if (bHasZ)
2923 : {
2924 236 : double z = 0.0;
2925 236 : memcpy(&z, pabyShape + nOffsetZ + i * 8, 8);
2926 236 : CPL_LSBPTR64(&z);
2927 236 : poPt->setZ(z);
2928 : }
2929 :
2930 : // Copy M.
2931 472 : if (bHasM)
2932 : {
2933 0 : double m = 0.0;
2934 0 : memcpy(&m, pabyShape + nOffsetM + i * 8, 8);
2935 0 : CPL_LSBPTR64(&m);
2936 0 : poPt->setM(m);
2937 : }
2938 :
2939 472 : poMultiPt->addGeometryDirectly(poPt);
2940 : }
2941 :
2942 236 : poMultiPt->set3D(bHasZ);
2943 236 : poMultiPt->setMeasured(bHasM);
2944 :
2945 236 : return OGRERR_NONE;
2946 : }
2947 :
2948 : /* ==================================================================== */
2949 : /* Extract vertices for a point. */
2950 : /* ==================================================================== */
2951 242 : else if (nSHPType == SHPT_POINT || nSHPType == SHPT_POINTM ||
2952 0 : nSHPType == SHPT_POINTZ || nSHPType == SHPT_POINTZM)
2953 : {
2954 242 : if (nBytes < 4 + 8 + 8 + ((bHasZ) ? 8 : 0) + ((bHasM) ? 8 : 0))
2955 : {
2956 0 : CPLError(CE_Failure, CPLE_AppDefined,
2957 : "Corrupted Shape : nBytes=%d, nSHPType=%d", nBytes,
2958 : nSHPType);
2959 0 : return OGRERR_FAILURE;
2960 : }
2961 :
2962 242 : double dfX = 0.0;
2963 242 : double dfY = 0.0;
2964 :
2965 242 : memcpy(&dfX, pabyShape + 4, 8);
2966 242 : memcpy(&dfY, pabyShape + 4 + 8, 8);
2967 :
2968 242 : CPL_LSBPTR64(&dfX);
2969 242 : CPL_LSBPTR64(&dfY);
2970 : // int nOffset = 20 + 8;
2971 :
2972 242 : double dfZ = 0.0;
2973 242 : if (bHasZ)
2974 : {
2975 118 : memcpy(&dfZ, pabyShape + 4 + 16, 8);
2976 118 : CPL_LSBPTR64(&dfZ);
2977 : }
2978 :
2979 242 : double dfM = 0.0;
2980 242 : if (bHasM)
2981 : {
2982 0 : memcpy(&dfM, pabyShape + 4 + 16 + ((bHasZ) ? 8 : 0), 8);
2983 0 : CPL_LSBPTR64(&dfM);
2984 : }
2985 :
2986 242 : if (bHasZ && bHasM)
2987 : {
2988 0 : *ppoGeom = new OGRPoint(dfX, dfY, dfZ, dfM);
2989 : }
2990 242 : else if (bHasZ)
2991 : {
2992 118 : *ppoGeom = new OGRPoint(dfX, dfY, dfZ);
2993 : }
2994 124 : else if (bHasM)
2995 : {
2996 0 : OGRPoint *poPoint = new OGRPoint(dfX, dfY);
2997 0 : poPoint->setM(dfM);
2998 0 : *ppoGeom = poPoint;
2999 : }
3000 : else
3001 : {
3002 124 : *ppoGeom = new OGRPoint(dfX, dfY);
3003 : }
3004 :
3005 242 : return OGRERR_NONE;
3006 : }
3007 :
3008 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported geometry type: %d",
3009 : nSHPType);
3010 :
3011 0 : return OGRERR_FAILURE;
3012 : }
3013 :
3014 : #ifdef HAVE_WFLAG_UNREACHABLE_CODE_AGGRESSIVE
3015 : #if defined(__clang__)
3016 : #pragma clang diagnostic pop
3017 : #endif
3018 : #endif
|