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