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