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