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