Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements translation of Shapefile shapes into OGR
5 : * representation.
6 : * Author: Frank Warmerdam, warmerda@home.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Les Technologies SoftMap Inc.
10 : * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_port.h"
16 : #include "ogrshape.h"
17 :
18 : #include <cmath>
19 : #include <cstdio>
20 : #include <cstdlib>
21 : #include <cstring>
22 : #include <algorithm>
23 : #include <limits>
24 : #include <memory>
25 : #include <utility>
26 :
27 : #include "cpl_conv.h"
28 : #include "cpl_error.h"
29 : #include "cpl_string.h"
30 : #include "cpl_minixml.h"
31 : #include "cpl_vsi_virtual.h"
32 : #include "ogr_core.h"
33 : #include "ogr_feature.h"
34 : #include "ogr_geometry.h"
35 : #include "ogrpgeogeometry.h"
36 : #include "ogrshape.h"
37 : #include "shapefil.h"
38 :
39 : /************************************************************************/
40 : /* RingStartEnd */
41 : /* Set first and last vertex for given ring. */
42 : /************************************************************************/
43 40344 : static void RingStartEnd(SHPObject *psShape, int ring, int *start, int *end)
44 : {
45 40344 : if (psShape->panPartStart == nullptr)
46 : {
47 0 : *start = 0;
48 0 : *end = psShape->nVertices - 1;
49 : }
50 : else
51 : {
52 40344 : *start = psShape->panPartStart[ring];
53 :
54 40344 : if (ring == psShape->nParts - 1)
55 39853 : *end = psShape->nVertices - 1;
56 : else
57 491 : *end = psShape->panPartStart[ring + 1] - 1;
58 : }
59 40344 : }
60 :
61 : /************************************************************************/
62 : /* CreateLinearRing */
63 : /************************************************************************/
64 : static std::unique_ptr<OGRLinearRing>
65 40344 : CreateLinearRing(SHPObject *psShape, int ring, bool bHasZ, bool bHasM)
66 : {
67 40344 : int nRingStart = 0;
68 40344 : int nRingEnd = 0;
69 40344 : RingStartEnd(psShape, ring, &nRingStart, &nRingEnd);
70 :
71 40344 : auto poRing = std::make_unique<OGRLinearRing>();
72 40344 : if (!(nRingEnd >= nRingStart))
73 0 : return poRing;
74 :
75 40344 : const int nRingPoints = nRingEnd - nRingStart + 1;
76 :
77 40344 : if (bHasZ && bHasM)
78 74 : poRing->setPoints(
79 40 : nRingPoints, psShape->padfX + nRingStart,
80 40 : psShape->padfY + nRingStart, psShape->padfZ + nRingStart,
81 40 : psShape->padfM ? psShape->padfM + nRingStart : nullptr);
82 40304 : else if (bHasM)
83 15 : poRing->setPointsM(nRingPoints, psShape->padfX + nRingStart,
84 12 : psShape->padfY + nRingStart,
85 12 : psShape->padfM ? psShape->padfM + nRingStart
86 : : nullptr);
87 : else
88 40292 : poRing->setPoints(nRingPoints, psShape->padfX + nRingStart,
89 40292 : psShape->padfY + nRingStart);
90 :
91 40344 : return poRing;
92 : }
93 :
94 : /************************************************************************/
95 : /* SHPReadOGRObject() */
96 : /* */
97 : /* Read an item in a shapefile, and translate to OGR geometry */
98 : /* representation. */
99 : /************************************************************************/
100 :
101 96070 : std::unique_ptr<OGRGeometry> SHPReadOGRObject(SHPHandle hSHP, int iShape,
102 : SHPObject *psShape,
103 : bool &bHasWarnedWrongWindingOrder,
104 : OGRwkbGeometryType eLayerGeomType)
105 : {
106 : #if DEBUG_VERBOSE
107 : CPLDebug("Shape", "SHPReadOGRObject( iShape=%d )", iShape);
108 : #endif
109 :
110 96070 : if (psShape == nullptr)
111 62782 : psShape = SHPReadObject(hSHP, iShape);
112 :
113 96070 : if (psShape == nullptr)
114 : {
115 11 : return nullptr;
116 : }
117 :
118 96059 : std::unique_ptr<OGRGeometry> poOGR;
119 :
120 : /* -------------------------------------------------------------------- */
121 : /* Point. */
122 : /* -------------------------------------------------------------------- */
123 96059 : if (psShape->nSHPType == SHPT_POINT)
124 : {
125 : poOGR =
126 6460 : std::make_unique<OGRPoint>(psShape->padfX[0], psShape->padfY[0]);
127 : }
128 89599 : else if (psShape->nSHPType == SHPT_POINTZ)
129 : {
130 43947 : if (psShape->bMeasureIsUsed)
131 : {
132 7 : poOGR = std::make_unique<OGRPoint>(
133 7 : psShape->padfX[0], psShape->padfY[0], psShape->padfZ[0],
134 7 : psShape->padfM[0]);
135 : }
136 : else
137 : {
138 43940 : poOGR = std::make_unique<OGRPoint>(
139 43940 : psShape->padfX[0], psShape->padfY[0], psShape->padfZ[0]);
140 : }
141 : }
142 45652 : else if (psShape->nSHPType == SHPT_POINTM)
143 : {
144 5 : poOGR = std::make_unique<OGRPoint>(psShape->padfX[0], psShape->padfY[0],
145 5 : 0.0, psShape->padfM[0]);
146 5 : poOGR->set3D(FALSE);
147 : }
148 : /* -------------------------------------------------------------------- */
149 : /* Multipoint. */
150 : /* -------------------------------------------------------------------- */
151 45647 : else if (psShape->nSHPType == SHPT_MULTIPOINT ||
152 45635 : psShape->nSHPType == SHPT_MULTIPOINTM ||
153 45633 : psShape->nSHPType == SHPT_MULTIPOINTZ)
154 : {
155 22 : if (psShape->nVertices == 0)
156 : {
157 1 : poOGR = nullptr;
158 : }
159 : else
160 : {
161 42 : auto poOGRMPoint = std::make_unique<OGRMultiPoint>();
162 :
163 54 : for (int i = 0; i < psShape->nVertices; i++)
164 : {
165 0 : std::unique_ptr<OGRPoint> poPoint;
166 :
167 33 : if (psShape->nSHPType == SHPT_MULTIPOINTZ)
168 : {
169 12 : if (psShape->padfM)
170 : {
171 2 : poPoint = std::make_unique<OGRPoint>(
172 2 : psShape->padfX[i], psShape->padfY[i],
173 2 : psShape->padfZ[i], psShape->padfM[i]);
174 : }
175 : else
176 : {
177 10 : poPoint = std::make_unique<OGRPoint>(psShape->padfX[i],
178 10 : psShape->padfY[i],
179 10 : psShape->padfZ[i]);
180 : }
181 : }
182 21 : else if (psShape->nSHPType == SHPT_MULTIPOINTM &&
183 2 : psShape->padfM)
184 : {
185 2 : poPoint = std::make_unique<OGRPoint>(psShape->padfX[i],
186 0 : psShape->padfY[i], 0.0,
187 2 : psShape->padfM[i]);
188 2 : poPoint->set3D(FALSE);
189 : }
190 : else
191 : {
192 19 : poPoint = std::make_unique<OGRPoint>(psShape->padfX[i],
193 19 : psShape->padfY[i]);
194 : }
195 :
196 33 : poOGRMPoint->addGeometry(std::move(poPoint));
197 : }
198 :
199 21 : poOGR = std::move(poOGRMPoint);
200 22 : }
201 : }
202 :
203 : /* -------------------------------------------------------------------- */
204 : /* Arc (LineString) */
205 : /* */
206 : /* Ignoring parts though they can apply to arcs as well. */
207 : /* -------------------------------------------------------------------- */
208 45625 : else if (psShape->nSHPType == SHPT_ARC || psShape->nSHPType == SHPT_ARCM ||
209 42751 : psShape->nSHPType == SHPT_ARCZ)
210 : {
211 4755 : if (psShape->nParts == 1)
212 : {
213 9460 : auto poOGRLine = std::make_unique<OGRLineString>();
214 :
215 4730 : if (psShape->nSHPType == SHPT_ARCZ)
216 1874 : poOGRLine->setPoints(psShape->nVertices, psShape->padfX,
217 1874 : psShape->padfY, psShape->padfZ,
218 1874 : psShape->padfM);
219 2856 : else if (psShape->nSHPType == SHPT_ARCM)
220 4 : poOGRLine->setPointsM(psShape->nVertices, psShape->padfX,
221 4 : psShape->padfY, psShape->padfM);
222 : else
223 2852 : poOGRLine->setPoints(psShape->nVertices, psShape->padfX,
224 2852 : psShape->padfY);
225 :
226 4730 : if (wkbFlatten(eLayerGeomType) == wkbMultiLineString)
227 : {
228 9326 : auto poOGRMulti = std::make_unique<OGRMultiLineString>();
229 4663 : poOGRMulti->addGeometry(std::move(poOGRLine));
230 4663 : poOGR = std::move(poOGRMulti);
231 : }
232 : else
233 : {
234 67 : poOGR = std::move(poOGRLine);
235 : }
236 : }
237 25 : else if (psShape->nParts > 1)
238 : {
239 48 : auto poOGRMulti = std::make_unique<OGRMultiLineString>();
240 :
241 73 : for (int iRing = 0; iRing < psShape->nParts; iRing++)
242 : {
243 49 : int nRingPoints = 0;
244 49 : int nRingStart = 0;
245 :
246 49 : auto poLine = std::make_unique<OGRLineString>();
247 :
248 49 : if (psShape->panPartStart == nullptr)
249 : {
250 0 : nRingPoints = psShape->nVertices;
251 0 : nRingStart = 0;
252 : }
253 : else
254 : {
255 49 : if (iRing == psShape->nParts - 1)
256 24 : nRingPoints =
257 24 : psShape->nVertices - psShape->panPartStart[iRing];
258 : else
259 25 : nRingPoints = psShape->panPartStart[iRing + 1] -
260 25 : psShape->panPartStart[iRing];
261 49 : nRingStart = psShape->panPartStart[iRing];
262 : }
263 :
264 49 : if (psShape->nSHPType == SHPT_ARCZ)
265 24 : poLine->setPoints(
266 14 : nRingPoints, psShape->padfX + nRingStart,
267 14 : psShape->padfY + nRingStart,
268 14 : psShape->padfZ + nRingStart,
269 14 : psShape->padfM ? psShape->padfM + nRingStart : nullptr);
270 35 : else if (psShape->nSHPType == SHPT_ARCM &&
271 8 : psShape->padfM != nullptr)
272 6 : poLine->setPointsM(nRingPoints, psShape->padfX + nRingStart,
273 6 : psShape->padfY + nRingStart,
274 6 : psShape->padfM + nRingStart);
275 : else
276 29 : poLine->setPoints(nRingPoints, psShape->padfX + nRingStart,
277 29 : psShape->padfY + nRingStart);
278 :
279 49 : poOGRMulti->addGeometry(std::move(poLine));
280 : }
281 :
282 24 : poOGR = std::move(poOGRMulti);
283 4755 : }
284 : }
285 :
286 : /* -------------------------------------------------------------------- */
287 : /* Polygon */
288 : /* */
289 : /* As for now Z coordinate is not handled correctly */
290 : /* -------------------------------------------------------------------- */
291 40870 : else if (psShape->nSHPType == SHPT_POLYGON ||
292 1040 : psShape->nSHPType == SHPT_POLYGONM ||
293 1032 : psShape->nSHPType == SHPT_POLYGONZ)
294 : {
295 39854 : const bool bHasZ = psShape->nSHPType == SHPT_POLYGONZ;
296 39854 : const bool bHasM = bHasZ || psShape->nSHPType == SHPT_POLYGONM;
297 :
298 : #if DEBUG_VERBOSE
299 : CPLDebug("Shape", "Shape type: polygon with nParts=%d",
300 : psShape->nParts);
301 : #endif
302 :
303 39854 : if (psShape->nParts == 1)
304 : {
305 : // Surely outer ring.
306 79370 : auto poOGRPoly = std::make_unique<OGRPolygon>();
307 39685 : poOGRPoly->addRing(CreateLinearRing(psShape, 0, bHasZ, bHasM));
308 :
309 39685 : if (wkbFlatten(eLayerGeomType) == wkbMultiPolygon)
310 : {
311 78230 : auto poOGRMulti = std::make_unique<OGRMultiPolygon>();
312 39115 : poOGRMulti->addGeometry(std::move(poOGRPoly));
313 39115 : poOGR = std::move(poOGRMulti);
314 : }
315 : else
316 : {
317 570 : poOGR = std::move(poOGRPoly);
318 : }
319 : }
320 169 : else if (psShape->nParts >= 1)
321 : {
322 336 : std::vector<std::unique_ptr<OGRGeometry>> apoPolygons;
323 168 : apoPolygons.reserve(psShape->nParts);
324 827 : for (int iRing = 0; iRing < psShape->nParts; iRing++)
325 : {
326 659 : auto poPoly = std::make_unique<OGRPolygon>();
327 659 : poPoly->addRing(CreateLinearRing(psShape, iRing, bHasZ, bHasM));
328 659 : apoPolygons.push_back(std::move(poPoly));
329 : }
330 :
331 : // Tries to detect bad geometries where a multi-part multipolygon is
332 : // written as a single-part multipolygon with its parts as inner
333 : // rings, like done by QGIS <= 3.28.11 with GDAL >= 3.7
334 : // Cf https://github.com/qgis/QGIS/issues/54537
335 168 : bool bUseSlowMethod = false;
336 168 : if (!bHasZ && !bHasM)
337 : {
338 154 : bool bFoundCW = false;
339 309 : for (int iRing = 1; iRing < psShape->nParts; iRing++)
340 : {
341 208 : if (apoPolygons[iRing]
342 : ->toPolygon()
343 208 : ->getExteriorRing()
344 208 : ->isClockwise())
345 : {
346 53 : bFoundCW = true;
347 53 : break;
348 : }
349 : }
350 154 : if (!bFoundCW)
351 : {
352 : // Only inner rings
353 101 : OGREnvelope sFirstEnvelope;
354 101 : OGREnvelope sCurEnvelope;
355 : const OGRLinearRing *poExteriorRing =
356 101 : apoPolygons[0]->toPolygon()->getExteriorRing();
357 101 : poExteriorRing->getEnvelope(&sFirstEnvelope);
358 201 : for (int iRing = 1; iRing < psShape->nParts; iRing++)
359 : {
360 102 : apoPolygons[iRing]->getEnvelope(&sCurEnvelope);
361 102 : if (!sFirstEnvelope.Intersects(sCurEnvelope))
362 : {
363 : // If the envelopes of the rings don't intersect,
364 : // then it is clearly a multi-part polygon
365 1 : bUseSlowMethod = true;
366 1 : break;
367 : }
368 : else
369 : {
370 : // Otherwise take 4 points at each extremity of
371 : // the inner rings and check if there are in the
372 : // outer ring. If none are within it, then it is
373 : // very likely a outer ring (or an invalid ring
374 : // which is neither a outer nor a inner ring)
375 101 : const auto poRing = apoPolygons[iRing]
376 : ->toPolygon()
377 101 : ->getExteriorRing();
378 101 : const auto nNumPoints = poRing->getNumPoints();
379 101 : OGRPoint p;
380 : OGRPoint leftPoint(
381 101 : std::numeric_limits<double>::infinity(), 0);
382 : OGRPoint rightPoint(
383 101 : -std::numeric_limits<double>::infinity(), 0);
384 : OGRPoint bottomPoint(
385 101 : 0, std::numeric_limits<double>::infinity());
386 : OGRPoint topPoint(
387 101 : 0, -std::numeric_limits<double>::infinity());
388 15697 : for (int iPoint = 0; iPoint < nNumPoints - 1;
389 : ++iPoint)
390 : {
391 15596 : poRing->getPoint(iPoint, &p);
392 34732 : if (p.getX() < leftPoint.getX() ||
393 19136 : (p.getX() == leftPoint.getX() &&
394 7607 : p.getY() < leftPoint.getY()))
395 : {
396 7866 : leftPoint = p;
397 : }
398 34884 : if (p.getX() > rightPoint.getX() ||
399 19288 : (p.getX() == rightPoint.getX() &&
400 3813 : p.getY() > rightPoint.getY()))
401 : {
402 3931 : rightPoint = p;
403 : }
404 34896 : if (p.getY() < bottomPoint.getY() ||
405 19300 : (p.getY() == bottomPoint.getY() &&
406 3894 : p.getX() > bottomPoint.getX()))
407 : {
408 3999 : bottomPoint = p;
409 : }
410 31015 : if (p.getY() > topPoint.getY() ||
411 15419 : (p.getY() == topPoint.getY() &&
412 3822 : p.getX() < topPoint.getX()))
413 : {
414 7807 : topPoint = p;
415 : }
416 : }
417 101 : if (!poExteriorRing->isPointInRing(&leftPoint) &&
418 1 : !poExteriorRing->isPointInRing(&rightPoint) &&
419 103 : !poExteriorRing->isPointInRing(&bottomPoint) &&
420 1 : !poExteriorRing->isPointInRing(&topPoint))
421 : {
422 1 : bUseSlowMethod = true;
423 1 : break;
424 : }
425 : }
426 : }
427 101 : if (bUseSlowMethod && !bHasWarnedWrongWindingOrder)
428 : {
429 1 : bHasWarnedWrongWindingOrder = true;
430 1 : CPLError(CE_Warning, CPLE_AppDefined,
431 : "%s contains polygon(s) with rings with "
432 : "invalid winding order. Autocorrecting them, "
433 : "but that shapefile should be corrected using "
434 : "ogr2ogr for example.",
435 : VSI_SHP_GetFilename(hSHP->fpSHP));
436 : }
437 : }
438 : }
439 :
440 168 : bool isValidGeometry = false;
441 168 : const char *const apszOptions[] = {
442 168 : bUseSlowMethod ? "METHOD=DEFAULT" : "METHOD=ONLY_CCW", nullptr};
443 336 : poOGR = OGRGeometryFactory::organizePolygons(
444 168 : apoPolygons, &isValidGeometry, apszOptions);
445 :
446 273 : if (poOGR && wkbFlatten(poOGR->getGeometryType()) == wkbPolygon &&
447 105 : wkbFlatten(eLayerGeomType) == wkbMultiPolygon)
448 : {
449 192 : auto poOGRMulti = std::make_unique<OGRMultiPolygon>();
450 96 : poOGRMulti->addGeometryDirectly(poOGR.release()->toPolygon());
451 96 : poOGR = std::move(poOGRMulti);
452 : }
453 :
454 168 : if (!isValidGeometry)
455 : {
456 0 : CPLError(
457 : CE_Warning, CPLE_AppDefined,
458 : "Geometry of polygon of fid %d cannot be translated to "
459 : "Simple Geometry. "
460 : "All polygons will be contained in a multipolygon.",
461 : iShape);
462 : }
463 39854 : }
464 : }
465 :
466 : /* -------------------------------------------------------------------- */
467 : /* MultiPatch */
468 : /* -------------------------------------------------------------------- */
469 1016 : else if (psShape->nSHPType == SHPT_MULTIPATCH)
470 : {
471 24 : poOGR = std::unique_ptr<OGRGeometry>(OGRCreateFromMultiPatch(
472 12 : psShape->nParts, psShape->panPartStart, psShape->panPartType,
473 12 : psShape->nVertices, psShape->padfX, psShape->padfY,
474 24 : psShape->padfZ));
475 : }
476 :
477 : /* -------------------------------------------------------------------- */
478 : /* Otherwise for now we just ignore the object. */
479 : /* -------------------------------------------------------------------- */
480 : else
481 : {
482 1004 : if (psShape->nSHPType != SHPT_NULL)
483 : {
484 0 : CPLDebug("OGR", "Unsupported shape type in SHPReadOGRObject()");
485 : }
486 :
487 : // Nothing returned.
488 : }
489 :
490 : /* -------------------------------------------------------------------- */
491 : /* Cleanup shape, and set feature id. */
492 : /* -------------------------------------------------------------------- */
493 96059 : SHPDestroyObject(psShape);
494 :
495 96059 : return poOGR;
496 : }
497 :
498 : /************************************************************************/
499 : /* CheckNonFiniteCoordinates() */
500 : /************************************************************************/
501 :
502 86564 : static bool CheckNonFiniteCoordinates(const double *v, size_t vsize)
503 : {
504 115 : static bool bAllowNonFiniteCoordinates = CPLTestBool(
505 86679 : CPLGetConfigOption("OGR_SHAPE_ALLOW_NON_FINITE_COORDINATES", "NO"));
506 : // Do not document this. Only for edge case testing
507 86564 : if (bAllowNonFiniteCoordinates)
508 : {
509 0 : return true;
510 : }
511 588568 : for (size_t i = 0; i < vsize; ++i)
512 : {
513 502008 : if (!std::isfinite(v[i]))
514 : {
515 4 : CPLError(CE_Failure, CPLE_NotSupported,
516 : "Coordinates with non-finite values are not allowed");
517 4 : return false;
518 : }
519 : }
520 86560 : return true;
521 : }
522 :
523 86528 : static bool CheckNonFiniteCoordinates(const std::vector<double> &v)
524 : {
525 86528 : return CheckNonFiniteCoordinates(v.data(), v.size());
526 : }
527 :
528 : /************************************************************************/
529 : /* SHPWriteOGRObject() */
530 : /************************************************************************/
531 67118 : static OGRErr SHPWriteOGRObject(SHPHandle hSHP, int iShape,
532 : const OGRGeometry *poGeom, bool bRewind,
533 : OGRwkbGeometryType eLayerGeomType)
534 :
535 : {
536 : /* ==================================================================== */
537 : /* Write "shape" with no geometry or with empty geometry */
538 : /* ==================================================================== */
539 67118 : if (poGeom == nullptr || poGeom->IsEmpty())
540 : {
541 : SHPObject *psShape =
542 717 : SHPCreateObject(SHPT_NULL, -1, 0, nullptr, nullptr, 0, nullptr,
543 : nullptr, nullptr, nullptr);
544 717 : const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
545 717 : SHPDestroyObject(psShape);
546 717 : if (nReturnedShapeID == -1)
547 : {
548 : // Assuming error is reported by SHPWriteObject().
549 0 : return OGRERR_FAILURE;
550 : }
551 : }
552 :
553 : /* ==================================================================== */
554 : /* Write point geometry. */
555 : /* ==================================================================== */
556 66401 : else if (hSHP->nShapeType == SHPT_POINT ||
557 65629 : hSHP->nShapeType == SHPT_POINTM || hSHP->nShapeType == SHPT_POINTZ)
558 : {
559 44724 : if (wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
560 : {
561 12 : CPLError(CE_Failure, CPLE_AppDefined,
562 : "Attempt to write non-point (%s) geometry to"
563 : " point shapefile.",
564 12 : poGeom->getGeometryName());
565 :
566 13 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
567 : }
568 :
569 44712 : const OGRPoint *poPoint = poGeom->toPoint();
570 44712 : const double dfX = poPoint->getX();
571 44712 : const double dfY = poPoint->getY();
572 44712 : const double dfZ = poPoint->getZ();
573 44712 : double dfM = -std::numeric_limits<double>::max();
574 44712 : double *pdfM = nullptr;
575 44717 : if (wkbHasM(eLayerGeomType) && (hSHP->nShapeType == SHPT_POINTM ||
576 5 : hSHP->nShapeType == SHPT_POINTZ))
577 : {
578 10 : if (poGeom->IsMeasured())
579 10 : dfM = poPoint->getM();
580 10 : pdfM = &dfM;
581 : }
582 89424 : if ((!std::isfinite(dfX) || !std::isfinite(dfY) ||
583 89425 : !std::isfinite(dfZ) || (pdfM && !std::isfinite(*pdfM))) &&
584 1 : !CPLTestBool(CPLGetConfigOption(
585 : "OGR_SHAPE_ALLOW_NON_FINITE_COORDINATES", "NO")))
586 : {
587 1 : CPLError(CE_Failure, CPLE_NotSupported,
588 : "Coordinates with non-finite values are not allowed");
589 1 : return OGRERR_FAILURE;
590 : }
591 : SHPObject *psShape =
592 44711 : SHPCreateObject(hSHP->nShapeType, -1, 0, nullptr, nullptr, 1, &dfX,
593 : &dfY, &dfZ, pdfM);
594 44711 : const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
595 44711 : SHPDestroyObject(psShape);
596 44711 : if (nReturnedShapeID == -1)
597 44711 : return OGRERR_FAILURE;
598 : }
599 : /* ==================================================================== */
600 : /* MultiPoint. */
601 : /* ==================================================================== */
602 21677 : else if (hSHP->nShapeType == SHPT_MULTIPOINT ||
603 21663 : hSHP->nShapeType == SHPT_MULTIPOINTM ||
604 21660 : hSHP->nShapeType == SHPT_MULTIPOINTZ)
605 : {
606 31 : if (wkbFlatten(poGeom->getGeometryType()) != wkbMultiPoint)
607 : {
608 8 : CPLError(CE_Failure, CPLE_AppDefined,
609 : "Attempt to write non-multipoint (%s) geometry to "
610 : "multipoint shapefile.",
611 8 : poGeom->getGeometryName());
612 :
613 8 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
614 : }
615 :
616 23 : const OGRMultiPoint *poMP = poGeom->toMultiPoint();
617 23 : const int nNumGeometries = poMP->getNumGeometries();
618 43 : const bool bHasZ = (hSHP->nShapeType == SHPT_MULTIPOINTM ||
619 20 : hSHP->nShapeType == SHPT_MULTIPOINTZ);
620 23 : const bool bHasM = wkbHasM(eLayerGeomType) && bHasZ;
621 23 : const bool bIsGeomMeasured = poGeom->IsMeasured();
622 :
623 23 : std::vector<double> adfX;
624 23 : std::vector<double> adfY;
625 23 : std::vector<double> adfZ;
626 23 : std::vector<double> adfM;
627 : try
628 : {
629 23 : adfX.reserve(nNumGeometries);
630 23 : adfY.reserve(nNumGeometries);
631 23 : if (bHasZ)
632 14 : adfZ.reserve(nNumGeometries);
633 23 : if (bHasM)
634 6 : adfM.reserve(nNumGeometries);
635 : }
636 0 : catch (const std::exception &e)
637 : {
638 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
639 0 : return OGRERR_FAILURE;
640 : }
641 :
642 57 : for (const OGRPoint *poPoint : *poMP)
643 : {
644 : // Ignore POINT EMPTY.
645 34 : if (!poPoint->IsEmpty())
646 : {
647 33 : adfX.push_back(poPoint->getX());
648 33 : adfY.push_back(poPoint->getY());
649 33 : if (bHasZ)
650 20 : adfZ.push_back(poPoint->getZ());
651 33 : if (bHasM)
652 : {
653 8 : if (bIsGeomMeasured)
654 8 : adfM.push_back(poPoint->getM());
655 : else
656 0 : adfM.push_back(-std::numeric_limits<double>::max());
657 : }
658 : }
659 : else
660 : {
661 1 : CPLDebug("OGR",
662 : "Ignored POINT EMPTY inside MULTIPOINT in shapefile "
663 : "writer.");
664 : }
665 : }
666 23 : if (!CheckNonFiniteCoordinates(adfX) ||
667 23 : !CheckNonFiniteCoordinates(adfY) ||
668 69 : !CheckNonFiniteCoordinates(adfZ) ||
669 23 : !CheckNonFiniteCoordinates(adfM))
670 : {
671 0 : return OGRERR_FAILURE;
672 : }
673 :
674 52 : SHPObject *psShape = SHPCreateObject(
675 : hSHP->nShapeType, -1, 0, nullptr, nullptr,
676 23 : static_cast<int>(adfX.size()), adfX.data(), adfY.data(),
677 20 : bHasZ ? adfZ.data() : nullptr, bHasM ? adfM.data() : nullptr);
678 23 : const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
679 23 : SHPDestroyObject(psShape);
680 :
681 23 : if (nReturnedShapeID == -1)
682 23 : return OGRERR_FAILURE;
683 : }
684 :
685 : /* ==================================================================== */
686 : /* Arcs */
687 : /* ==================================================================== */
688 21646 : else if (hSHP->nShapeType == SHPT_ARC || hSHP->nShapeType == SHPT_ARCM ||
689 19121 : hSHP->nShapeType == SHPT_ARCZ)
690 : {
691 0 : std::unique_ptr<OGRGeometry> poGeomToDelete; // keep in that scope
692 4412 : const OGRMultiLineString *poML = nullptr;
693 4412 : OGRMultiLineString oMLFromLineString;
694 4412 : const auto eFlatGeomType = wkbFlatten(poGeom->getGeometryType());
695 4412 : if (eFlatGeomType == wkbMultiLineString)
696 : {
697 97 : poML = poGeom->toMultiLineString();
698 : }
699 4315 : else if (eFlatGeomType == wkbLineString)
700 : {
701 : // Borrow the geometry
702 4299 : oMLFromLineString.addGeometryDirectly(
703 4299 : const_cast<OGRLineString *>(poGeom->toLineString()));
704 4299 : poML = &oMLFromLineString;
705 : }
706 : else
707 : {
708 32 : poGeomToDelete = std::unique_ptr<OGRGeometry>(
709 32 : OGRGeometryFactory::forceToMultiLineString(poGeom->clone()));
710 16 : if (wkbFlatten(poGeomToDelete->getGeometryType()) !=
711 : wkbMultiLineString)
712 : {
713 9 : CPLError(CE_Failure, CPLE_AppDefined,
714 : "Attempt to write non-linestring (%s) geometry to "
715 : "ARC type shapefile.",
716 9 : poGeom->getGeometryName());
717 :
718 9 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
719 : }
720 7 : poML = poGeomToDelete->toMultiLineString();
721 : }
722 :
723 4403 : const int nNumGeometries = poML->getNumGeometries();
724 :
725 4403 : int nTotalPoints = 0;
726 8825 : for (const auto poArc : poML)
727 : {
728 4422 : const int nNumPoints = poArc->getNumPoints();
729 4422 : if (nTotalPoints > std::numeric_limits<int>::max() - nNumPoints)
730 : {
731 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too big geometry");
732 0 : return OGRERR_FAILURE;
733 : }
734 4422 : nTotalPoints += nNumPoints;
735 : }
736 :
737 4403 : std::vector<int> anRingStart;
738 4403 : std::vector<double> adfX;
739 4403 : std::vector<double> adfY;
740 4403 : std::vector<double> adfZ;
741 4403 : std::vector<double> adfM;
742 4403 : const bool bHasZ =
743 4403 : (hSHP->nShapeType == SHPT_ARCM || hSHP->nShapeType == SHPT_ARCZ);
744 4403 : const bool bHasM = wkbHasM(eLayerGeomType) && bHasZ;
745 4403 : const bool bIsGeomMeasured = poGeom->IsMeasured();
746 :
747 : try
748 : {
749 4403 : anRingStart.reserve(nNumGeometries);
750 :
751 4403 : adfX.reserve(nTotalPoints);
752 4403 : adfY.reserve(nTotalPoints);
753 4403 : if (bHasZ)
754 : {
755 1890 : adfZ.reserve(nTotalPoints);
756 : }
757 4403 : if (bHasM)
758 : {
759 10 : adfM.reserve(nTotalPoints);
760 : }
761 : }
762 0 : catch (const std::exception &e)
763 : {
764 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
765 : // Give back the borrowed line string
766 0 : if (eFlatGeomType == wkbLineString)
767 0 : oMLFromLineString.removeGeometry(0, /* bDelete=*/false);
768 0 : return OGRERR_FAILURE;
769 : }
770 :
771 8825 : for (const auto poArc : poML)
772 : {
773 4422 : const int nNumPoints = poArc->getNumPoints();
774 :
775 : // Ignore LINESTRING EMPTY.
776 4422 : if (nNumPoints == 0)
777 : {
778 1 : CPLDebug("OGR",
779 : "Ignore LINESTRING EMPTY inside MULTILINESTRING in "
780 : "shapefile writer.");
781 1 : continue;
782 : }
783 :
784 4421 : anRingStart.push_back(static_cast<int>(adfX.size()));
785 :
786 81551 : for (int iPoint = 0; iPoint < nNumPoints; iPoint++)
787 : {
788 77130 : adfX.push_back(poArc->getX(iPoint));
789 77130 : adfY.push_back(poArc->getY(iPoint));
790 77130 : if (bHasZ)
791 : {
792 49833 : adfZ.push_back(poArc->getZ(iPoint));
793 : }
794 77130 : if (bHasM)
795 : {
796 28 : if (bIsGeomMeasured)
797 28 : adfM.push_back(poArc->getM(iPoint));
798 : else
799 0 : adfM.push_back(-std::numeric_limits<double>::max());
800 : }
801 : }
802 : }
803 :
804 : // Give back the borrowed line string
805 4403 : if (eFlatGeomType == wkbLineString)
806 4299 : oMLFromLineString.removeGeometry(0, /* bDelete=*/false);
807 :
808 4403 : if (!CheckNonFiniteCoordinates(adfX) ||
809 4403 : !CheckNonFiniteCoordinates(adfY) ||
810 13207 : !CheckNonFiniteCoordinates(adfZ) ||
811 4401 : !CheckNonFiniteCoordinates(adfM))
812 : {
813 2 : return OGRERR_FAILURE;
814 : }
815 :
816 8812 : SHPObject *psShape = SHPCreateObject(
817 4401 : hSHP->nShapeType, iShape, static_cast<int>(anRingStart.size()),
818 4401 : anRingStart.data(), nullptr, static_cast<int>(adfX.size()),
819 4401 : adfX.data(), adfY.data(), bHasZ ? adfZ.data() : nullptr,
820 10 : bHasM ? adfM.data() : nullptr);
821 4401 : const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
822 4401 : SHPDestroyObject(psShape);
823 :
824 4401 : if (nReturnedShapeID == -1)
825 4401 : return OGRERR_FAILURE;
826 : }
827 :
828 : /* ==================================================================== */
829 : /* Polygons/MultiPolygons */
830 : /* ==================================================================== */
831 17234 : else if (hSHP->nShapeType == SHPT_POLYGON ||
832 96 : hSHP->nShapeType == SHPT_POLYGONM ||
833 91 : hSHP->nShapeType == SHPT_POLYGONZ)
834 : {
835 : // bool = true means outer ring
836 17221 : std::vector<std::pair<const OGRLinearRing *, bool>> apoRings;
837 17221 : const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
838 0 : std::unique_ptr<OGRGeometry> poGeomToDelete;
839 :
840 17221 : if (eType == wkbPolygon || eType == wkbTriangle)
841 : {
842 14731 : const OGRPolygon *poPoly = poGeom->toPolygon();
843 :
844 29462 : if (poPoly->getExteriorRing() == nullptr ||
845 14731 : poPoly->getExteriorRing()->IsEmpty())
846 : {
847 1 : CPLDebug("OGR", "Ignore POLYGON EMPTY in shapefile writer.");
848 : }
849 : else
850 : {
851 14730 : const int nSrcRings = poPoly->getNumInteriorRings() + 1;
852 14730 : apoRings.reserve(nSrcRings);
853 14730 : bool bFirstRing = true;
854 29466 : for (const auto poRing : poPoly)
855 : {
856 14736 : const int nNumPoints = poRing->getNumPoints();
857 :
858 : // Ignore LINEARRING EMPTY.
859 14736 : if (nNumPoints != 0)
860 : {
861 14736 : apoRings.push_back(std::make_pair(poRing, bFirstRing));
862 : }
863 : else
864 : {
865 0 : CPLDebug("OGR",
866 : "Ignore LINEARRING EMPTY inside POLYGON in "
867 : "shapefile writer.");
868 : }
869 14736 : bFirstRing = false;
870 : }
871 14731 : }
872 : }
873 2490 : else if (eType == wkbMultiPolygon || eType == wkbGeometryCollection ||
874 10 : eType == wkbPolyhedralSurface || eType == wkbTIN)
875 : {
876 : const OGRGeometryCollection *poGC;
877 : // for PolyhedralSurface and TIN
878 2480 : if (eType == wkbPolyhedralSurface || eType == wkbTIN)
879 : {
880 0 : poGeomToDelete = OGRGeometryFactory::forceTo(
881 0 : std::unique_ptr<OGRGeometry>(poGeom->clone()),
882 0 : wkbMultiPolygon, nullptr);
883 0 : poGC = poGeomToDelete->toGeometryCollection();
884 : }
885 :
886 : else
887 2480 : poGC = poGeom->toGeometryCollection();
888 :
889 : // Shouldn't happen really, but to please x86_64-w64-mingw32-g++ -O2
890 : // -Wnull-dereference
891 2480 : if (poGC == nullptr)
892 3 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
893 :
894 5061 : for (const auto poSubGeom : poGC)
895 : {
896 2584 : if (wkbFlatten(poSubGeom->getGeometryType()) != wkbPolygon)
897 : {
898 3 : CPLError(CE_Failure, CPLE_AppDefined,
899 : "Attempt to write non-polygon (%s) geometry to "
900 : "POLYGON type shapefile.",
901 3 : poSubGeom->getGeometryName());
902 :
903 3 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
904 : }
905 2581 : const OGRPolygon *poPoly = poSubGeom->toPolygon();
906 :
907 : // Ignore POLYGON EMPTY.
908 5161 : if (poPoly->getExteriorRing() == nullptr ||
909 2580 : poPoly->getExteriorRing()->IsEmpty())
910 : {
911 1 : CPLDebug("OGR",
912 : "Ignore POLYGON EMPTY inside MULTIPOLYGON in "
913 : "shapefile writer.");
914 1 : continue;
915 : }
916 :
917 2580 : const int nNumInteriorRings = poPoly->getNumInteriorRings();
918 : // to avoid coverity scan warning: "To avoid a quadratic time
919 : // penalty when using reserve(), always increase the capacity
920 : /// by a multiple of its current value"
921 2580 : if (apoRings.size() + nNumInteriorRings + 1 >
922 5109 : apoRings.capacity() &&
923 2529 : apoRings.size() < std::numeric_limits<size_t>::max() / 2)
924 : {
925 2529 : apoRings.reserve(std::max(
926 2529 : 2 * apoRings.size(), apoRings.size() + apoRings.size() +
927 5058 : nNumInteriorRings + 1));
928 : }
929 2580 : bool bFirstRing = true;
930 5288 : for (const auto poRing : poPoly)
931 : {
932 2708 : const int nNumPoints = poRing->getNumPoints();
933 :
934 : // Ignore LINEARRING EMPTY.
935 2708 : if (nNumPoints != 0)
936 : {
937 2706 : apoRings.push_back(std::make_pair(poRing, bFirstRing));
938 : }
939 : else
940 : {
941 2 : CPLDebug("OGR",
942 : "Ignore LINEARRING EMPTY inside POLYGON in "
943 : "shapefile writer.");
944 : }
945 2708 : bFirstRing = false;
946 : }
947 2477 : }
948 : }
949 : else
950 : {
951 10 : CPLError(CE_Failure, CPLE_AppDefined,
952 : "Attempt to write non-polygon (%s) geometry to "
953 : "POLYGON type shapefile.",
954 10 : poGeom->getGeometryName());
955 :
956 10 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
957 : }
958 :
959 : /* --------------------------------------------------------------------
960 : */
961 : /* If we only had emptypolygons or unacceptable geometries */
962 : /* write NULL geometry object. */
963 : /* --------------------------------------------------------------------
964 : */
965 17208 : if (apoRings.empty())
966 : {
967 : SHPObject *psShape =
968 1 : SHPCreateObject(SHPT_NULL, -1, 0, nullptr, nullptr, 0, nullptr,
969 : nullptr, nullptr, nullptr);
970 1 : const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
971 1 : SHPDestroyObject(psShape);
972 :
973 1 : if (nReturnedShapeID == -1)
974 0 : return OGRERR_FAILURE;
975 :
976 1 : return OGRERR_NONE;
977 : }
978 :
979 : // Count vertices.
980 17207 : int nVertex = 0;
981 34649 : for (const auto &ring : apoRings)
982 17442 : nVertex += ring.first->getNumPoints();
983 :
984 34409 : const bool bHasZ = (hSHP->nShapeType == SHPT_POLYGONM ||
985 17202 : hSHP->nShapeType == SHPT_POLYGONZ);
986 17207 : const bool bHasM = wkbHasM(eLayerGeomType) && bHasZ;
987 17207 : const bool bIsGeomMeasured = poGeom->IsMeasured();
988 :
989 17207 : std::vector<int> anRingStart;
990 17207 : std::vector<double> adfX;
991 17207 : std::vector<double> adfY;
992 17207 : std::vector<double> adfZ;
993 17207 : std::vector<double> adfM;
994 : try
995 : {
996 17207 : anRingStart.reserve(apoRings.size());
997 17207 : adfX.reserve(nVertex);
998 17207 : adfY.reserve(nVertex);
999 17207 : if (bHasZ)
1000 79 : adfZ.reserve(nVertex);
1001 17207 : if (bHasM)
1002 10 : adfM.reserve(nVertex);
1003 : }
1004 0 : catch (const std::exception &e)
1005 : {
1006 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1007 0 : return OGRERR_FAILURE;
1008 : }
1009 :
1010 : // Collect vertices.
1011 34649 : for (const auto &ring : apoRings)
1012 : {
1013 17442 : const auto poRing = ring.first;
1014 17442 : const bool bIsOuterRing = ring.second;
1015 17442 : anRingStart.push_back(static_cast<int>(adfX.size()));
1016 :
1017 17442 : const int nNumPoints = poRing->getNumPoints();
1018 : // Exterior ring must be clockwise oriented in shapefiles
1019 : const bool bInvertOrder =
1020 34884 : !bRewind &&
1021 17442 : (bIsOuterRing ? !poRing->isClockwise() : poRing->isClockwise());
1022 165508 : for (int i = 0; i < nNumPoints; i++)
1023 : {
1024 148066 : const int iPoint = bInvertOrder ? nNumPoints - 1 - i : i;
1025 148066 : adfX.push_back(poRing->getX(iPoint));
1026 148066 : adfY.push_back(poRing->getY(iPoint));
1027 148066 : if (bHasZ)
1028 1453 : adfZ.push_back(poRing->getZ(iPoint));
1029 148066 : if (bHasM)
1030 : {
1031 56 : adfM.push_back(bIsGeomMeasured
1032 56 : ? poRing->getM(iPoint)
1033 0 : : -std::numeric_limits<double>::max());
1034 : }
1035 : }
1036 : }
1037 17207 : if (!CheckNonFiniteCoordinates(adfX) ||
1038 17207 : !CheckNonFiniteCoordinates(adfY) ||
1039 51619 : !CheckNonFiniteCoordinates(adfZ) ||
1040 17205 : !CheckNonFiniteCoordinates(adfM))
1041 : {
1042 2 : return OGRERR_FAILURE;
1043 : }
1044 :
1045 34420 : SHPObject *psShape = SHPCreateObject(
1046 17205 : hSHP->nShapeType, iShape, static_cast<int>(anRingStart.size()),
1047 17205 : anRingStart.data(), nullptr, static_cast<int>(adfX.size()),
1048 17205 : adfX.data(), adfY.data(), bHasZ ? adfZ.data() : nullptr,
1049 10 : bHasM ? adfM.data() : nullptr);
1050 17205 : if (bRewind)
1051 0 : SHPRewindObject(hSHP, psShape);
1052 17205 : const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
1053 17205 : SHPDestroyObject(psShape);
1054 :
1055 17205 : if (nReturnedShapeID == -1)
1056 17205 : return OGRERR_FAILURE;
1057 : }
1058 :
1059 : /* ==================================================================== */
1060 : /* Multipatch */
1061 : /* ==================================================================== */
1062 13 : else if (hSHP->nShapeType == SHPT_MULTIPATCH)
1063 : {
1064 13 : int nParts = 0;
1065 13 : std::vector<int> anPartStart;
1066 13 : std::vector<int> anPartType;
1067 13 : int nPoints = 0;
1068 13 : std::vector<OGRRawPoint> aoPoints;
1069 13 : std::vector<double> adfZ;
1070 13 : OGRErr eErr = OGRCreateMultiPatch(poGeom,
1071 : FALSE, // no SHPP_TRIANGLES
1072 : nParts, anPartStart, anPartType,
1073 : nPoints, aoPoints, adfZ);
1074 13 : if (eErr != OGRERR_NONE)
1075 1 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
1076 :
1077 12 : std::vector<double> adfX(nPoints);
1078 12 : std::vector<double> adfY(nPoints);
1079 64 : for (int i = 0; i < nPoints; ++i)
1080 : {
1081 52 : adfX[i] = aoPoints[i].x;
1082 52 : adfY[i] = aoPoints[i].y;
1083 : }
1084 :
1085 12 : if (!CheckNonFiniteCoordinates(adfX.data(), nPoints) ||
1086 24 : !CheckNonFiniteCoordinates(adfY.data(), nPoints) ||
1087 12 : !CheckNonFiniteCoordinates(adfZ.data(), nPoints))
1088 : {
1089 0 : return OGRERR_FAILURE;
1090 : }
1091 :
1092 : SHPObject *psShape =
1093 12 : SHPCreateObject(hSHP->nShapeType, iShape, nParts,
1094 12 : anPartStart.data(), anPartType.data(), nPoints,
1095 12 : adfX.data(), adfY.data(), adfZ.data(), nullptr);
1096 12 : if (bRewind)
1097 5 : SHPRewindObject(hSHP, psShape);
1098 12 : const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
1099 12 : SHPDestroyObject(psShape);
1100 :
1101 12 : if (nReturnedShapeID == -1)
1102 0 : return OGRERR_FAILURE;
1103 : }
1104 :
1105 : else
1106 : {
1107 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
1108 : }
1109 :
1110 67069 : return OGRERR_NONE;
1111 : }
1112 :
1113 : /************************************************************************/
1114 : /* SHPReadOGRFeatureDefn() */
1115 : /************************************************************************/
1116 :
1117 : OGRFeatureDefnRefCountedPtr
1118 7568 : SHPReadOGRFeatureDefn(const char *pszName, SHPHandle hSHP, DBFHandle hDBF,
1119 : VSILFILE *fpSHPXML, const char *pszSHPEncoding,
1120 : int bAdjustType)
1121 :
1122 : {
1123 7568 : int nAdjustableFields = 0;
1124 7568 : const int nFieldCount = hDBF ? DBFGetFieldCount(hDBF) : 0;
1125 :
1126 7568 : auto poDefn = OGRFeatureDefnRefCountedPtr::makeInstance(pszName);
1127 :
1128 : // Parse .shp.xml side car if available, to get long field names and aliases
1129 : // but only if they are consistent with the number of DBF fields and their
1130 : // content.
1131 15136 : std::vector<OGRFieldDefn> aFieldsFromSHPXML;
1132 7568 : if (fpSHPXML)
1133 : {
1134 2 : fpSHPXML->Seek(0, SEEK_END);
1135 2 : const auto nSize = fpSHPXML->Tell();
1136 2 : if (nSize < 10 * 1024 * 1024)
1137 : {
1138 2 : fpSHPXML->Seek(0, SEEK_SET);
1139 2 : GByte *pabyOut = nullptr;
1140 2 : if (VSIIngestFile(fpSHPXML, nullptr, &pabyOut, nullptr, -1))
1141 : {
1142 2 : const char *pszXML = reinterpret_cast<char *>(pabyOut);
1143 4 : CPLXMLTreeCloser oTree(CPLParseXMLString(pszXML));
1144 2 : if (oTree)
1145 : {
1146 : const CPLXMLNode *psFields =
1147 2 : CPLGetXMLNode(oTree.get(), "=metadata.eainfo.detailed");
1148 2 : int iField = 0;
1149 2 : for (const CPLXMLNode *psIter = psFields ? psFields->psChild
1150 : : nullptr;
1151 19 : psIter; psIter = psIter->psNext)
1152 : {
1153 17 : if (psIter->eType != CXT_Element ||
1154 15 : strcmp(psIter->pszValue, "attr") != 0)
1155 8 : continue;
1156 : const char *pszType =
1157 13 : CPLGetXMLValue(psIter, "attrtype", "");
1158 13 : if (strcmp(pszType, "OID") == 0 ||
1159 11 : strcmp(pszType, "Geometry") == 0)
1160 4 : continue;
1161 : const char *pszLabel =
1162 9 : CPLGetXMLValue(psIter, "attrlabl", "");
1163 9 : if (iField == nFieldCount)
1164 : {
1165 0 : CPLDebug("Shape",
1166 : "More fields in .shp.xml than in .dbf");
1167 0 : aFieldsFromSHPXML.clear();
1168 0 : break;
1169 : }
1170 :
1171 9 : char szFieldNameFromDBF[XBASE_FLDNAME_LEN_READ + 1] =
1172 : {};
1173 9 : int nWidth = 0;
1174 9 : int nPrecision = 0;
1175 9 : CPL_IGNORE_RET_VAL(
1176 9 : DBFGetFieldInfo(hDBF, iField, szFieldNameFromDBF,
1177 : &nWidth, &nPrecision));
1178 9 : std::string osFieldNameFromDBF(szFieldNameFromDBF);
1179 9 : if (strlen(pszSHPEncoding) > 0)
1180 : {
1181 : char *pszUTF8Field =
1182 9 : CPLRecode(szFieldNameFromDBF, pszSHPEncoding,
1183 : CPL_ENC_UTF8);
1184 9 : osFieldNameFromDBF = pszUTF8Field;
1185 9 : CPLFree(pszUTF8Field);
1186 : }
1187 : // Check that field names are consistent
1188 26 : if ((strlen(pszLabel) < 10 &&
1189 18 : pszLabel != osFieldNameFromDBF) ||
1190 10 : (strlen(pszLabel) >= 10 &&
1191 1 : osFieldNameFromDBF.size() >= 10 &&
1192 1 : strncmp(pszLabel, osFieldNameFromDBF.c_str(), 5) !=
1193 : 0))
1194 : {
1195 0 : CPLDebug("Shape",
1196 : "For field at index %d, mismatch between "
1197 : ".shp.xml name (%s) vs .dbf name (%s)",
1198 : iField, pszLabel, szFieldNameFromDBF);
1199 0 : aFieldsFromSHPXML.clear();
1200 0 : break;
1201 : }
1202 :
1203 9 : OGRFieldDefn oField(pszLabel, OFTMaxType);
1204 : const char *pszAlias =
1205 9 : CPLGetXMLValue(psIter, "attalias", "");
1206 9 : if (pszAlias[0] && strcmp(pszLabel, pszAlias) != 0)
1207 1 : oField.SetAlternativeName(pszAlias);
1208 : const char *pszWidth =
1209 9 : CPLGetXMLValue(psIter, "attwidth", "");
1210 9 : if (strcmp(pszType, "Integer") == 0 &&
1211 1 : strcmp(pszWidth, "4") == 0)
1212 1 : oField.SetType(OFTInteger);
1213 9 : aFieldsFromSHPXML.push_back(std::move(oField));
1214 9 : ++iField;
1215 : }
1216 2 : if (iField != nFieldCount)
1217 0 : aFieldsFromSHPXML.clear();
1218 : }
1219 : }
1220 2 : CPLFree(pabyOut);
1221 : }
1222 : }
1223 :
1224 19193 : for (int iField = 0; iField < nFieldCount; iField++)
1225 : {
1226 : // On reading we support up to 11 characters
1227 11625 : char szFieldName[XBASE_FLDNAME_LEN_READ + 1] = {};
1228 11625 : int nWidth = 0;
1229 11625 : int nPrecision = 0;
1230 : DBFFieldType eDBFType =
1231 11625 : DBFGetFieldInfo(hDBF, iField, szFieldName, &nWidth, &nPrecision);
1232 :
1233 23250 : OGRFieldDefn oField("", OFTInteger);
1234 11625 : if (!aFieldsFromSHPXML.empty())
1235 : {
1236 9 : oField = aFieldsFromSHPXML[iField];
1237 : }
1238 11616 : else if (strlen(pszSHPEncoding) > 0)
1239 : {
1240 : char *const pszUTF8Field =
1241 6921 : CPLRecode(szFieldName, pszSHPEncoding, CPL_ENC_UTF8);
1242 6921 : oField.SetName(pszUTF8Field);
1243 6921 : CPLFree(pszUTF8Field);
1244 : }
1245 : else
1246 : {
1247 4695 : oField.SetName(szFieldName);
1248 : }
1249 :
1250 11625 : oField.SetWidth(nWidth);
1251 11625 : oField.SetPrecision(nPrecision);
1252 :
1253 11625 : if (eDBFType == FTDate)
1254 : {
1255 : // Shapefile date has following 8-chars long format:
1256 : //
1257 : // 20060101.
1258 : //
1259 : // Split as YYYY/MM/DD, so 2 additional characters are required.
1260 56 : oField.SetWidth(nWidth + 2);
1261 56 : oField.SetType(OFTDate);
1262 : }
1263 11569 : else if (eDBFType == FTDouble)
1264 : {
1265 4257 : nAdjustableFields += (nPrecision == 0);
1266 4257 : if (nPrecision == 0 && nWidth < 19)
1267 : {
1268 2287 : if (!aFieldsFromSHPXML.empty() &&
1269 1 : aFieldsFromSHPXML[iField].GetType() == OFTInteger)
1270 0 : oField.SetType(OFTInteger);
1271 : else
1272 2286 : oField.SetType(OFTInteger64);
1273 : }
1274 : else
1275 1971 : oField.SetType(OFTReal);
1276 : }
1277 7312 : else if (eDBFType == FTInteger)
1278 1170 : oField.SetType(OFTInteger);
1279 6142 : else if (eDBFType == FTLogical)
1280 : {
1281 2 : oField.SetType(OFTInteger);
1282 2 : oField.SetSubType(OFSTBoolean);
1283 : }
1284 : else
1285 6140 : oField.SetType(OFTString);
1286 :
1287 11625 : poDefn->AddFieldDefn(&oField);
1288 : }
1289 :
1290 : // Do an optional past if requested and needed to demote Integer64->Integer
1291 : // or Real->Integer64/Integer.
1292 7568 : if (nAdjustableFields && bAdjustType)
1293 : {
1294 : int *panAdjustableField =
1295 1 : static_cast<int *>(CPLCalloc(sizeof(int), nFieldCount));
1296 3 : for (int iField = 0; iField < nFieldCount; iField++)
1297 : {
1298 2 : OGRFieldType eType = poDefn->GetFieldDefn(iField)->GetType();
1299 3 : if (poDefn->GetFieldDefn(iField)->GetPrecision() == 0 &&
1300 1 : (eType == OFTInteger64 || eType == OFTReal))
1301 : {
1302 2 : panAdjustableField[iField] = TRUE;
1303 2 : poDefn->GetFieldDefn(iField)->SetType(OFTInteger);
1304 : }
1305 : }
1306 :
1307 1 : const int nRowCount = DBFGetRecordCount(hDBF);
1308 2 : for (int iRow = 0; iRow < nRowCount && nAdjustableFields; iRow++)
1309 : {
1310 3 : for (int iField = 0; iField < nFieldCount; iField++)
1311 : {
1312 2 : if (panAdjustableField[iField])
1313 : {
1314 : const char *pszValue =
1315 2 : DBFReadStringAttribute(hDBF, iRow, iField);
1316 2 : const int nValueLength = static_cast<int>(strlen(pszValue));
1317 2 : if (nValueLength >= 10)
1318 : {
1319 2 : int bOverflow = FALSE;
1320 : const GIntBig nVal =
1321 2 : CPLAtoGIntBigEx(pszValue, FALSE, &bOverflow);
1322 2 : if (bOverflow)
1323 : {
1324 0 : poDefn->GetFieldDefn(iField)->SetType(OFTReal);
1325 0 : panAdjustableField[iField] = FALSE;
1326 0 : nAdjustableFields--;
1327 : }
1328 2 : else if (!CPL_INT64_FITS_ON_INT32(nVal))
1329 : {
1330 1 : poDefn->GetFieldDefn(iField)->SetType(OFTInteger64);
1331 1 : if (poDefn->GetFieldDefn(iField)->GetWidth() <= 18)
1332 : {
1333 0 : panAdjustableField[iField] = FALSE;
1334 0 : nAdjustableFields--;
1335 : }
1336 : }
1337 : }
1338 : }
1339 : }
1340 : }
1341 :
1342 1 : CPLFree(panAdjustableField);
1343 : }
1344 :
1345 7568 : if (hSHP == nullptr)
1346 : {
1347 789 : poDefn->SetGeomType(wkbNone);
1348 : }
1349 : else
1350 : {
1351 6779 : switch (hSHP->nShapeType)
1352 : {
1353 1703 : case SHPT_POINT:
1354 1703 : poDefn->SetGeomType(wkbPoint);
1355 1703 : break;
1356 :
1357 118 : case SHPT_POINTZ:
1358 118 : poDefn->SetGeomType(wkbPointZM);
1359 118 : break;
1360 :
1361 10 : case SHPT_POINTM:
1362 10 : poDefn->SetGeomType(wkbPointM);
1363 10 : break;
1364 :
1365 2841 : case SHPT_ARC:
1366 2841 : poDefn->SetGeomType(wkbLineString);
1367 2841 : break;
1368 :
1369 90 : case SHPT_ARCZ:
1370 90 : poDefn->SetGeomType(wkbLineStringZM);
1371 90 : break;
1372 :
1373 14 : case SHPT_ARCM:
1374 14 : poDefn->SetGeomType(wkbLineStringM);
1375 14 : break;
1376 :
1377 116 : case SHPT_MULTIPOINT:
1378 116 : poDefn->SetGeomType(wkbMultiPoint);
1379 116 : break;
1380 :
1381 81 : case SHPT_MULTIPOINTZ:
1382 81 : poDefn->SetGeomType(wkbMultiPointZM);
1383 81 : break;
1384 :
1385 6 : case SHPT_MULTIPOINTM:
1386 6 : poDefn->SetGeomType(wkbMultiPointM);
1387 6 : break;
1388 :
1389 1682 : case SHPT_POLYGON:
1390 1682 : poDefn->SetGeomType(wkbPolygon);
1391 1682 : break;
1392 :
1393 84 : case SHPT_POLYGONZ:
1394 84 : poDefn->SetGeomType(wkbPolygonZM);
1395 84 : break;
1396 :
1397 14 : case SHPT_POLYGONM:
1398 14 : poDefn->SetGeomType(wkbPolygonM);
1399 14 : break;
1400 :
1401 20 : case SHPT_MULTIPATCH:
1402 20 : poDefn->SetGeomType(wkbUnknown); // not ideal
1403 20 : break;
1404 : }
1405 : }
1406 :
1407 15136 : return poDefn;
1408 : }
1409 :
1410 : /************************************************************************/
1411 : /* SHPReadOGRFeature() */
1412 : /************************************************************************/
1413 :
1414 100368 : OGRFeature *SHPReadOGRFeature(SHPHandle hSHP, DBFHandle hDBF,
1415 : OGRFeatureDefn *poDefn, int iShape,
1416 : SHPObject *psShape, const char *pszSHPEncoding,
1417 : bool &bHasWarnedWrongWindingOrder)
1418 :
1419 : {
1420 100368 : if (iShape < 0 || (hSHP != nullptr && iShape >= hSHP->nRecords) ||
1421 100301 : (hDBF != nullptr && iShape >= hDBF->nRecords))
1422 : {
1423 25 : CPLError(CE_Failure, CPLE_AppDefined,
1424 : "Attempt to read shape with feature id (%d) out of available"
1425 : " range.",
1426 : iShape);
1427 25 : return nullptr;
1428 : }
1429 :
1430 100343 : if (hDBF && DBFIsRecordDeleted(hDBF, iShape))
1431 : {
1432 2 : CPLError(CE_Failure, CPLE_AppDefined,
1433 : "Attempt to read shape with feature id (%d), "
1434 : "but it is marked deleted.",
1435 : iShape);
1436 2 : if (psShape != nullptr)
1437 0 : SHPDestroyObject(psShape);
1438 2 : return nullptr;
1439 : }
1440 :
1441 100341 : OGRFeature *poFeature = new OGRFeature(poDefn);
1442 :
1443 : /* -------------------------------------------------------------------- */
1444 : /* Fetch geometry from Shapefile to OGRFeature. */
1445 : /* -------------------------------------------------------------------- */
1446 100341 : if (hSHP != nullptr)
1447 : {
1448 96537 : if (!poDefn->IsGeometryIgnored())
1449 : {
1450 : auto poGeometry = SHPReadOGRObject(hSHP, iShape, psShape,
1451 : bHasWarnedWrongWindingOrder,
1452 95510 : poDefn->GetGeomType());
1453 :
1454 : // Two possibilities are expected here (both are tested by
1455 : // GDAL Autotests):
1456 : // 1. Read valid geometry and assign it directly.
1457 : // 2. Read and assign null geometry if it can not be read
1458 : // correctly from a shapefile.
1459 : //
1460 : // It is NOT required here to test poGeometry == NULL.
1461 :
1462 95510 : if (poGeometry)
1463 : {
1464 : // Set/unset flags.
1465 : const OGRwkbGeometryType eMyGeomType =
1466 94492 : poFeature->GetDefnRef()->GetGeomFieldDefn(0)->GetType();
1467 :
1468 94492 : if (eMyGeomType != wkbUnknown)
1469 : {
1470 : OGRwkbGeometryType eGeomInType =
1471 94480 : poGeometry->getGeometryType();
1472 94480 : if (wkbHasZ(eMyGeomType) && !wkbHasZ(eGeomInType))
1473 : {
1474 0 : poGeometry->set3D(TRUE);
1475 : }
1476 94480 : else if (!wkbHasZ(eMyGeomType) && wkbHasZ(eGeomInType))
1477 : {
1478 0 : poGeometry->set3D(FALSE);
1479 : }
1480 94480 : if (wkbHasM(eMyGeomType) && !wkbHasM(eGeomInType))
1481 : {
1482 0 : poGeometry->setMeasured(TRUE);
1483 : }
1484 94480 : else if (!wkbHasM(eMyGeomType) && wkbHasM(eGeomInType))
1485 : {
1486 1 : poGeometry->setMeasured(FALSE);
1487 : }
1488 : }
1489 : }
1490 :
1491 95510 : poFeature->SetGeometry(std::move(poGeometry));
1492 : }
1493 1027 : else if (psShape != nullptr)
1494 : {
1495 10 : SHPDestroyObject(psShape);
1496 : }
1497 : }
1498 :
1499 : /* -------------------------------------------------------------------- */
1500 : /* Fetch feature attributes to OGRFeature fields. */
1501 : /* -------------------------------------------------------------------- */
1502 :
1503 252240 : for (int iField = 0; hDBF != nullptr && iField < poDefn->GetFieldCount();
1504 : iField++)
1505 : {
1506 151899 : const OGRFieldDefn *const poFieldDefn = poDefn->GetFieldDefn(iField);
1507 151899 : if (poFieldDefn->IsIgnored())
1508 1976 : continue;
1509 :
1510 149923 : switch (poFieldDefn->GetType())
1511 : {
1512 18673 : case OFTString:
1513 : {
1514 : const char *const pszFieldVal =
1515 18673 : DBFReadStringAttribute(hDBF, iShape, iField);
1516 18673 : if (pszFieldVal != nullptr && pszFieldVal[0] != '\0')
1517 : {
1518 16825 : if (pszSHPEncoding[0] != '\0')
1519 : {
1520 4940 : char *const pszUTF8Field = CPLRecode(
1521 : pszFieldVal, pszSHPEncoding, CPL_ENC_UTF8);
1522 4940 : poFeature->SetField(iField, pszUTF8Field);
1523 4940 : CPLFree(pszUTF8Field);
1524 : }
1525 : else
1526 16825 : poFeature->SetField(iField, pszFieldVal);
1527 : }
1528 : else
1529 : {
1530 1848 : poFeature->SetFieldNull(iField);
1531 : }
1532 18673 : break;
1533 : }
1534 131231 : case OFTInteger:
1535 : case OFTInteger64:
1536 : case OFTReal:
1537 : {
1538 131231 : if (DBFIsAttributeNULL(hDBF, iShape, iField))
1539 : {
1540 382 : poFeature->SetFieldNull(iField);
1541 : }
1542 : else
1543 : {
1544 130849 : if (poFieldDefn->GetSubType() == OFSTBoolean)
1545 : {
1546 : const char *pszVal =
1547 2 : DBFReadLogicalAttribute(hDBF, iShape, iField);
1548 2 : poFeature->SetField(
1549 2 : iField, pszVal[0] == 'T' || pszVal[0] == 't' ||
1550 1 : pszVal[0] == 'Y' || pszVal[0] == 'y'
1551 : ? 1
1552 : : 0);
1553 : }
1554 : else
1555 : {
1556 : const char *pszVal =
1557 130847 : DBFReadStringAttribute(hDBF, iShape, iField);
1558 130847 : poFeature->SetField(iField, pszVal);
1559 : }
1560 : }
1561 131231 : break;
1562 : }
1563 19 : case OFTDate:
1564 : {
1565 19 : if (DBFIsAttributeNULL(hDBF, iShape, iField))
1566 : {
1567 16 : poFeature->SetFieldNull(iField);
1568 16 : continue;
1569 : }
1570 :
1571 : const char *const pszDateValue =
1572 3 : DBFReadStringAttribute(hDBF, iShape, iField);
1573 :
1574 : OGRField sFld;
1575 3 : memset(&sFld, 0, sizeof(sFld));
1576 :
1577 3 : if (strlen(pszDateValue) >= 10 && pszDateValue[2] == '/' &&
1578 1 : pszDateValue[5] == '/')
1579 : {
1580 1 : sFld.Date.Month =
1581 1 : static_cast<GByte>(atoi(pszDateValue + 0));
1582 1 : sFld.Date.Day = static_cast<GByte>(atoi(pszDateValue + 3));
1583 1 : sFld.Date.Year =
1584 1 : static_cast<GInt16>(atoi(pszDateValue + 6));
1585 : }
1586 : else
1587 : {
1588 2 : const int nFullDate = atoi(pszDateValue);
1589 2 : sFld.Date.Year = static_cast<GInt16>(nFullDate / 10000);
1590 2 : sFld.Date.Month =
1591 2 : static_cast<GByte>((nFullDate / 100) % 100);
1592 2 : sFld.Date.Day = static_cast<GByte>(nFullDate % 100);
1593 : }
1594 :
1595 3 : poFeature->SetField(iField, &sFld);
1596 : }
1597 3 : break;
1598 :
1599 0 : default:
1600 0 : CPLAssert(false);
1601 : }
1602 : }
1603 :
1604 100341 : if (poFeature != nullptr)
1605 100341 : poFeature->SetFID(iShape);
1606 :
1607 100341 : return poFeature;
1608 : }
1609 :
1610 : /************************************************************************/
1611 : /* GrowField() */
1612 : /************************************************************************/
1613 :
1614 6 : static OGRErr GrowField(DBFHandle hDBF, int iField, OGRFieldDefn *poFieldDefn,
1615 : int nNewSize)
1616 : {
1617 6 : char szFieldName[20] = {};
1618 6 : int nOriWidth = 0;
1619 6 : int nPrecision = 0;
1620 6 : DBFGetFieldInfo(hDBF, iField, szFieldName, &nOriWidth, &nPrecision);
1621 :
1622 6 : CPLDebug("SHAPE", "Extending field %d (%s) from %d to %d characters",
1623 : iField, poFieldDefn->GetNameRef(), nOriWidth, nNewSize);
1624 :
1625 6 : const char chNativeType = DBFGetNativeFieldType(hDBF, iField);
1626 6 : if (!DBFAlterFieldDefn(hDBF, iField, szFieldName, chNativeType, nNewSize,
1627 : nPrecision))
1628 : {
1629 0 : CPLError(CE_Failure, CPLE_AppDefined,
1630 : "Extending field %d (%s) from %d to %d characters failed",
1631 : iField, poFieldDefn->GetNameRef(), nOriWidth, nNewSize);
1632 0 : return OGRERR_FAILURE;
1633 : }
1634 :
1635 6 : auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
1636 6 : poFieldDefn->SetWidth(nNewSize);
1637 6 : return OGRERR_NONE;
1638 : }
1639 :
1640 : /************************************************************************/
1641 : /* SHPWriteOGRFeature() */
1642 : /* */
1643 : /* Write to an existing feature in a shapefile, or create a new */
1644 : /* feature. */
1645 : /************************************************************************/
1646 :
1647 69253 : OGRErr SHPWriteOGRFeature(SHPHandle hSHP, DBFHandle hDBF,
1648 : OGRFeatureDefn *poDefn, OGRFeature *poFeature,
1649 : const char *pszSHPEncoding,
1650 : bool *pbTruncationWarningEmitted, bool bRewind)
1651 :
1652 : {
1653 : /* -------------------------------------------------------------------- */
1654 : /* Write the geometry. */
1655 : /* -------------------------------------------------------------------- */
1656 69253 : if (hSHP != nullptr)
1657 : {
1658 67118 : const OGRErr eErr = SHPWriteOGRObject(
1659 67118 : hSHP, static_cast<int>(poFeature->GetFID()),
1660 67118 : poFeature->GetGeometryRef(), bRewind, poDefn->GetGeomType());
1661 67118 : if (eErr != OGRERR_NONE)
1662 48 : return eErr;
1663 : }
1664 :
1665 : /* -------------------------------------------------------------------- */
1666 : /* If there is no DBF, the job is done now. */
1667 : /* -------------------------------------------------------------------- */
1668 69205 : if (hDBF == nullptr)
1669 : {
1670 : /* --------------------------------------------------------------------
1671 : */
1672 : /* If this is a new feature, establish its feature id. */
1673 : /* --------------------------------------------------------------------
1674 : */
1675 15 : if (hSHP != nullptr && poFeature->GetFID() == OGRNullFID)
1676 1 : poFeature->SetFID(hSHP->nRecords - 1);
1677 :
1678 15 : return OGRERR_NONE;
1679 : }
1680 :
1681 : /* -------------------------------------------------------------------- */
1682 : /* If this is a new feature, establish its feature id. */
1683 : /* -------------------------------------------------------------------- */
1684 69190 : if (poFeature->GetFID() == OGRNullFID)
1685 69144 : poFeature->SetFID(DBFGetRecordCount(hDBF));
1686 :
1687 : /* -------------------------------------------------------------------- */
1688 : /* If this is the first feature to be written, verify that we */
1689 : /* have at least one attribute in the DBF file. If not, create */
1690 : /* a dummy FID attribute to satisfy the requirement that there */
1691 : /* be at least one attribute. */
1692 : /* -------------------------------------------------------------------- */
1693 69190 : if (DBFGetRecordCount(hDBF) == 0 && DBFGetFieldCount(hDBF) == 0)
1694 : {
1695 236 : CPLDebug(
1696 : "OGR",
1697 : "Created dummy FID field for shapefile since schema is empty.");
1698 236 : DBFAddField(hDBF, "FID", FTInteger, 11, 0);
1699 : }
1700 :
1701 : /* -------------------------------------------------------------------- */
1702 : /* Write out dummy field value if it exists. */
1703 : /* -------------------------------------------------------------------- */
1704 69190 : if (poDefn->GetFieldCount() == 0)
1705 : {
1706 56523 : if (DBFGetFieldCount(hDBF) == 1)
1707 : {
1708 56522 : DBFWriteIntegerAttribute(hDBF,
1709 56522 : static_cast<int>(poFeature->GetFID()), 0,
1710 56522 : static_cast<int>(poFeature->GetFID()));
1711 : }
1712 1 : else if (DBFGetFieldCount(hDBF) == 0)
1713 : {
1714 : // Far from being nominal... Could happen if deleting all fields
1715 : // of a DBF with rows
1716 1 : DBFWriteAttributeDirectly(
1717 1 : hDBF, static_cast<int>(poFeature->GetFID()), -1, nullptr);
1718 : }
1719 : }
1720 :
1721 : /* -------------------------------------------------------------------- */
1722 : /* Write all the fields. */
1723 : /* -------------------------------------------------------------------- */
1724 94251 : for (int iField = 0; iField < poDefn->GetFieldCount(); iField++)
1725 : {
1726 25061 : if (!poFeature->IsFieldSetAndNotNull(iField))
1727 : {
1728 817 : DBFWriteNULLAttribute(hDBF, static_cast<int>(poFeature->GetFID()),
1729 : iField);
1730 817 : continue;
1731 : }
1732 :
1733 24244 : OGRFieldDefn *const poFieldDefn = poDefn->GetFieldDefn(iField);
1734 :
1735 24244 : switch (poFieldDefn->GetType())
1736 : {
1737 4709 : case OFTString:
1738 : {
1739 4709 : const char *pszStr = poFeature->GetFieldAsString(iField);
1740 4709 : char *pszEncoded = nullptr;
1741 4709 : if (pszSHPEncoding[0] != '\0')
1742 : {
1743 : pszEncoded =
1744 4699 : CPLRecode(pszStr, CPL_ENC_UTF8, pszSHPEncoding);
1745 4699 : pszStr = pszEncoded;
1746 : }
1747 :
1748 4709 : int nStrLen = static_cast<int>(strlen(pszStr));
1749 4709 : if (nStrLen > OGR_DBF_MAX_FIELD_WIDTH)
1750 : {
1751 3 : if (!(*pbTruncationWarningEmitted))
1752 : {
1753 2 : *pbTruncationWarningEmitted = true;
1754 2 : CPLError(
1755 : CE_Warning, CPLE_AppDefined,
1756 : "Value '%s' of field %s has been truncated to %d "
1757 : "characters. This warning will not be emitted any "
1758 : "more for that layer.",
1759 : poFeature->GetFieldAsString(iField),
1760 : poFieldDefn->GetNameRef(), OGR_DBF_MAX_FIELD_WIDTH);
1761 : }
1762 :
1763 3 : nStrLen = OGR_DBF_MAX_FIELD_WIDTH;
1764 :
1765 3 : if (pszEncoded != nullptr && // For Coverity.
1766 3 : EQUAL(pszSHPEncoding, CPL_ENC_UTF8))
1767 : {
1768 : // Truncate string by making sure we don't cut in the
1769 : // middle of a UTF-8 multibyte character
1770 : // Continuation bytes of such characters are of the form
1771 : // 10xxxxxx (0x80), whereas single-byte are 0xxxxxxx
1772 : // and the start of a multi-byte is 11xxxxxx
1773 2 : const char *p = pszStr + nStrLen;
1774 2 : while (nStrLen > 0)
1775 : {
1776 2 : if ((*p & 0xc0) != 0x80)
1777 : {
1778 2 : break;
1779 : }
1780 :
1781 0 : nStrLen--;
1782 0 : p--;
1783 : }
1784 :
1785 2 : pszEncoded[nStrLen] = 0;
1786 : }
1787 : }
1788 :
1789 4709 : if (nStrLen > poFieldDefn->GetWidth())
1790 : {
1791 2 : if (GrowField(hDBF, iField, poFieldDefn, nStrLen) !=
1792 : OGRERR_NONE)
1793 : {
1794 0 : CPLFree(pszEncoded);
1795 0 : return OGRERR_FAILURE;
1796 : }
1797 : }
1798 :
1799 4709 : DBFWriteStringAttribute(hDBF,
1800 4709 : static_cast<int>(poFeature->GetFID()),
1801 : iField, pszStr);
1802 :
1803 4709 : CPLFree(pszEncoded);
1804 4709 : break;
1805 : }
1806 15351 : case OFTInteger:
1807 : case OFTInteger64:
1808 : {
1809 15351 : if (poFieldDefn->GetSubType() == OFSTBoolean)
1810 : {
1811 4 : DBFWriteAttributeDirectly(
1812 2 : hDBF, static_cast<int>(poFeature->GetFID()), iField,
1813 2 : poFeature->GetFieldAsInteger(iField) ? "T" : "F");
1814 : }
1815 : else
1816 : {
1817 15349 : char szValue[32] = {};
1818 15349 : const int nFieldWidth = poFieldDefn->GetWidth();
1819 15349 : snprintf(szValue, sizeof(szValue),
1820 : "%*" CPL_FRMT_GB_WITHOUT_PREFIX "d",
1821 : std::min(nFieldWidth,
1822 15349 : static_cast<int>(sizeof(szValue)) - 1),
1823 : poFeature->GetFieldAsInteger64(iField));
1824 :
1825 15349 : const int nStrLen = static_cast<int>(strlen(szValue));
1826 15349 : if (nStrLen > nFieldWidth)
1827 : {
1828 4 : if (GrowField(hDBF, iField, poFieldDefn, nStrLen) !=
1829 : OGRERR_NONE)
1830 : {
1831 0 : return OGRERR_FAILURE;
1832 : }
1833 : }
1834 :
1835 15349 : DBFWriteAttributeDirectly(
1836 15349 : hDBF, static_cast<int>(poFeature->GetFID()), iField,
1837 : szValue);
1838 : }
1839 :
1840 15351 : break;
1841 : }
1842 :
1843 4149 : case OFTReal:
1844 : {
1845 4149 : const double dfVal = poFeature->GetFieldAsDouble(iField);
1846 : // IEEE754 doubles can store exact values of all integers
1847 : // below 2^53.
1848 4151 : if (poFieldDefn->GetPrecision() == 0 &&
1849 2 : fabs(dfVal) > (static_cast<GIntBig>(1) << 53))
1850 : {
1851 : static int nCounter = 0;
1852 1 : if (nCounter <= 10)
1853 : {
1854 1 : CPLError(CE_Warning, CPLE_AppDefined,
1855 : "Value %.17g of field %s with 0 decimal of "
1856 : "feature " CPL_FRMT_GIB
1857 : " is bigger than 2^53. "
1858 : "Precision loss likely occurred or going to "
1859 : "happen.%s",
1860 : dfVal, poFieldDefn->GetNameRef(),
1861 : poFeature->GetFID(),
1862 1 : (nCounter == 10) ? " This warning will not be "
1863 : "emitted anymore."
1864 : : "");
1865 1 : nCounter++;
1866 : }
1867 : }
1868 4149 : int ret = DBFWriteDoubleAttribute(
1869 4149 : hDBF, static_cast<int>(poFeature->GetFID()), iField, dfVal);
1870 4149 : if (!ret)
1871 : {
1872 6 : CPLError(CE_Warning, CPLE_AppDefined,
1873 : "Value %.17g of field %s of feature " CPL_FRMT_GIB
1874 : " not "
1875 : "successfully written. Possibly due to too larger "
1876 : "number "
1877 : "with respect to field width",
1878 : dfVal, poFieldDefn->GetNameRef(),
1879 : poFeature->GetFID());
1880 : }
1881 4149 : break;
1882 : }
1883 35 : case OFTDate:
1884 : {
1885 : const OGRField *const psField =
1886 35 : poFeature->GetRawFieldRef(iField);
1887 :
1888 35 : if (psField->Date.Year < 0 || psField->Date.Year > 9999)
1889 : {
1890 0 : CPLError(
1891 : CE_Warning, CPLE_NotSupported,
1892 : "Year < 0 or > 9999 is not a valid date for shapefile");
1893 : }
1894 35 : else if (psField->Date.Year == 0 && psField->Date.Month == 0 &&
1895 0 : psField->Date.Day == 0)
1896 : {
1897 0 : DBFWriteNULLAttribute(
1898 0 : hDBF, static_cast<int>(poFeature->GetFID()), iField);
1899 : }
1900 : else
1901 : {
1902 35 : DBFWriteIntegerAttribute(
1903 35 : hDBF, static_cast<int>(poFeature->GetFID()), iField,
1904 35 : psField->Date.Year * 10000 + psField->Date.Month * 100 +
1905 35 : psField->Date.Day);
1906 : }
1907 : }
1908 35 : break;
1909 :
1910 0 : default:
1911 : {
1912 : // Ignore fields of other types.
1913 0 : break;
1914 : }
1915 : }
1916 : }
1917 :
1918 69190 : return OGRERR_NONE;
1919 : }
|