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