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