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