Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CSV Translator
4 : * Purpose: Implements OGRCSVLayer class.
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "ogr_csv.h"
16 :
17 : #include <cerrno>
18 : #include <climits>
19 : #include <cstddef>
20 : #include <cstdio>
21 : #include <cstdlib>
22 : #include <cstring>
23 : #if HAVE_FCNTL_H
24 : #include <fcntl.h>
25 : #endif
26 : #include <algorithm>
27 : #include <cinttypes>
28 : #include <limits>
29 : #include <string>
30 : #include <vector>
31 :
32 : #include "cpl_conv.h"
33 : #include "cpl_csv.h"
34 : #include "cpl_error.h"
35 : #include "cpl_string.h"
36 : #include "cpl_vsi.h"
37 : #include "cpl_vsi_virtual.h"
38 : #include "ogr_api.h"
39 : #include "ogr_core.h"
40 : #include "ogr_feature.h"
41 : #include "ogr_geometry.h"
42 : #include "ogr_p.h"
43 : #include "ogr_spatialref.h"
44 : #include "ogrsf_frmts.h"
45 :
46 : #define DIGIT_ZERO '0'
47 :
48 : /************************************************************************/
49 : /* OGRCSVLayer() */
50 : /* */
51 : /* Note that the OGRCSVLayer assumes ownership of the passed */
52 : /* file pointer. */
53 : /************************************************************************/
54 :
55 781 : OGRCSVLayer::OGRCSVLayer(GDALDataset *poDS, const char *pszLayerNameIn,
56 : VSILFILE *fp, int nMaxLineSize,
57 : const char *pszFilenameIn, int bNewIn,
58 781 : int bInWriteModeIn, char chDelimiterIn)
59 : : m_poDS(poDS), poFeatureDefn(nullptr), fpCSV(fp),
60 : m_nMaxLineSize(nMaxLineSize), bHasFieldNames(false),
61 1562 : bNew(CPL_TO_BOOL(bNewIn)), bInWriteMode(CPL_TO_BOOL(bInWriteModeIn)),
62 : bUseCRLF(false), bNeedRewindBeforeRead(false),
63 1562 : eGeometryFormat(OGR_CSV_GEOM_NONE), pszFilename(CPLStrdup(pszFilenameIn)),
64 : bCreateCSVT(false), bWriteBOM(false), nCSVFieldCount(0),
65 : panGeomFieldIndex(nullptr), bFirstFeatureAppendedDuringSession(true),
66 : bHiddenWKTColumn(false), iNfdcLongitudeS(-1), iNfdcLatitudeS(-1),
67 : bHonourStrings(true), iLongitudeField(-1), iLatitudeField(-1),
68 : iZField(-1), bIsEurostatTSV(false), nEurostatDims(0),
69 781 : nTotalFeatures(bNewIn ? 0 : -1), bWarningBadTypeOrWidth(false),
70 : bKeepSourceColumns(false), bKeepGeomColumns(true), bMergeDelimiter(false),
71 781 : bEmptyStringNull(false)
72 : {
73 781 : szDelimiter[0] = chDelimiterIn;
74 781 : szDelimiter[1] = 0;
75 781 : poFeatureDefn = new OGRFeatureDefn(pszLayerNameIn);
76 781 : SetDescription(poFeatureDefn->GetName());
77 781 : poFeatureDefn->Reference();
78 781 : poFeatureDefn->SetGeomType(wkbNone);
79 781 : }
80 :
81 : /************************************************************************/
82 : /* Matches() */
83 : /************************************************************************/
84 :
85 10935 : bool OGRCSVLayer::Matches(const char *pszFieldName, char **papszPossibleNames)
86 : {
87 10935 : if (papszPossibleNames == nullptr)
88 0 : return false;
89 11047 : for (char **papszIter = papszPossibleNames; *papszIter; papszIter++)
90 : {
91 154 : const char *pszPattern = *papszIter;
92 154 : const char *pszStar = strstr(pszPattern, "*");
93 154 : if (pszStar == nullptr)
94 : {
95 145 : if (EQUAL(pszFieldName, pszPattern))
96 39 : return true;
97 : }
98 : else
99 : {
100 9 : if (pszStar == pszPattern)
101 : {
102 6 : if (strlen(pszPattern) >= 3 &&
103 6 : pszPattern[strlen(pszPattern) - 1] == '*')
104 : {
105 : // *pattern*
106 3 : CPLString oPattern(pszPattern + 1);
107 3 : oPattern.pop_back();
108 3 : if (CPLString(pszFieldName).ifind(oPattern) !=
109 : std::string::npos)
110 3 : return true;
111 : }
112 : else
113 : {
114 : // *pattern
115 3 : if (strlen(pszFieldName) >= strlen(pszPattern) - 1 &&
116 1 : EQUAL(pszFieldName + strlen(pszFieldName) -
117 : (strlen(pszPattern) - 1),
118 : pszPattern + 1))
119 : {
120 1 : return true;
121 : }
122 : }
123 : }
124 3 : else if (pszPattern[strlen(pszPattern) - 1] == '*')
125 : {
126 : // pattern*
127 3 : if (EQUALN(pszFieldName, pszPattern, strlen(pszPattern) - 1))
128 1 : return true;
129 : }
130 : }
131 : }
132 10893 : return false;
133 : }
134 :
135 : /************************************************************************/
136 : /* BuildFeatureDefn() */
137 : /************************************************************************/
138 :
139 781 : void OGRCSVLayer::BuildFeatureDefn(const char *pszNfdcGeomField,
140 : const char *pszGeonamesGeomFieldPrefix,
141 : CSLConstList papszOpenOptions)
142 : {
143 781 : bMergeDelimiter = CPLFetchBool(papszOpenOptions, "MERGE_SEPARATOR", false);
144 781 : bEmptyStringNull =
145 781 : CPLFetchBool(papszOpenOptions, "EMPTY_STRING_AS_NULL", false);
146 :
147 : // If this is not a new file, read ahead to establish if it is
148 : // already in CRLF (DOS) mode, or just a normal unix CR mode.
149 781 : if (!bNew && bInWriteMode)
150 : {
151 97 : int nBytesRead = 0;
152 97 : char chNewByte = '\0';
153 :
154 55787 : while (nBytesRead < 10000 && VSIFReadL(&chNewByte, 1, 1, fpCSV) == 1)
155 : {
156 55691 : if (chNewByte == 13)
157 : {
158 1 : bUseCRLF = true;
159 1 : break;
160 : }
161 55690 : nBytesRead++;
162 : }
163 97 : VSIRewindL(fpCSV);
164 : }
165 :
166 : // Check if the first record seems to be field definitions or
167 : // not. We assume it is field definitions if the HEADERS option
168 : // not supplied and none of the values are strictly numeric.
169 781 : char **papszTokens = nullptr;
170 781 : int nFieldCount = 0;
171 :
172 781 : if (!bNew)
173 : {
174 648 : const char *pszLine = CPLReadLineL(fpCSV);
175 648 : if (pszLine != nullptr)
176 : {
177 : // Detect and remove UTF-8 BOM marker if found (#4623).
178 648 : if (reinterpret_cast<const unsigned char *>(pszLine)[0] == 0xEF &&
179 2 : reinterpret_cast<const unsigned char *>(pszLine)[1] == 0xBB &&
180 2 : reinterpret_cast<const unsigned char *>(pszLine)[2] == 0xBF)
181 : {
182 2 : pszLine += 3;
183 : }
184 :
185 : // Tokenize the strings and preserve quotes, so we can separate
186 : // string from numeric this is only used in the test for
187 : // bHasFieldNames (bug #4361).
188 : papszTokens =
189 648 : CSLTokenizeString2(pszLine, szDelimiter,
190 : (CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS |
191 : CSLT_PRESERVEQUOTES));
192 648 : nFieldCount = CSLCount(papszTokens);
193 :
194 648 : if (nFieldCount > 0 && papszTokens[0][0] == '"')
195 29 : m_eStringQuoting = StringQuoting::ALWAYS;
196 :
197 : const char *pszCSVHeaders =
198 648 : CSLFetchNameValueDef(papszOpenOptions, "HEADERS", "AUTO");
199 :
200 648 : if (EQUAL(pszCSVHeaders, "YES"))
201 : {
202 0 : bHasFieldNames = true;
203 : }
204 648 : else if (EQUAL(pszCSVHeaders, "NO"))
205 : {
206 0 : bHasFieldNames = false;
207 : }
208 : else
209 : {
210 : // Detect via checking for the presence of numeric values.
211 648 : bHasFieldNames = true;
212 3492 : for (int iField = 0; iField < nFieldCount && bHasFieldNames;
213 : iField++)
214 : {
215 : const CPLValueType eType =
216 2844 : CPLGetValueType(papszTokens[iField]);
217 2844 : if (eType == CPL_VALUE_INTEGER || eType == CPL_VALUE_REAL)
218 : {
219 : // We have a numeric field, therefore do not consider
220 : // the first line as field names.
221 66 : bHasFieldNames = false;
222 : }
223 : }
224 :
225 : const CPLString osExt =
226 1296 : OGRCSVDataSource::GetRealExtension(pszFilename);
227 :
228 : // Eurostat .tsv files.
229 649 : if (EQUAL(osExt, "tsv") && nFieldCount > 1 &&
230 650 : strchr(papszTokens[0], ',') != nullptr &&
231 1 : strchr(papszTokens[0], '\\') != nullptr)
232 : {
233 1 : bHasFieldNames = true;
234 1 : bIsEurostatTSV = true;
235 : }
236 : }
237 :
238 : // Tokenize without quotes to get the actual values.
239 648 : VSIRewindL(fpCSV);
240 648 : CSLDestroy(papszTokens);
241 : papszTokens =
242 1296 : CSVReadParseLine3L(fpCSV, m_nMaxLineSize, szDelimiter,
243 : true, // bHonourStrings
244 : false, // bKeepLeadingAndClosingQuotes
245 648 : bMergeDelimiter,
246 : true // bSkipBOM
247 : );
248 648 : nFieldCount = CSLCount(papszTokens);
249 : }
250 : }
251 : else
252 : {
253 133 : bHasFieldNames = false;
254 : }
255 :
256 781 : if (!bNew)
257 648 : ResetReading();
258 :
259 : int nMaxFieldCount =
260 781 : atoi(CPLGetConfigOption("OGR_CSV_MAX_FIELD_COUNT", "2000"));
261 781 : if (nFieldCount > nMaxFieldCount)
262 : {
263 0 : CPLError(CE_Warning, CPLE_AppDefined,
264 : "%d columns detected. Limiting to %d. "
265 : "Set OGR_CSV_MAX_FIELD_COUNT configuration option "
266 : "to allow more fields.",
267 : nFieldCount, nMaxFieldCount);
268 0 : nFieldCount = nMaxFieldCount;
269 : }
270 781 : if (nFieldCount > 100000)
271 0 : nFieldCount = 100000; // to please coverity
272 :
273 781 : nCSVFieldCount = nFieldCount;
274 :
275 : // coverity[tainted_data]
276 781 : panGeomFieldIndex = static_cast<int *>(CPLCalloc(nFieldCount, sizeof(int)));
277 3949 : for (int iField = 0; iField < nFieldCount; iField++)
278 : {
279 3168 : panGeomFieldIndex[iField] = -1;
280 : }
281 :
282 : // Check for geonames.org tables.
283 781 : if (!bHasFieldNames && nFieldCount == 19)
284 : {
285 1 : if (CPLGetValueType(papszTokens[0]) == CPL_VALUE_INTEGER &&
286 1 : CPLGetValueType(papszTokens[4]) == CPL_VALUE_REAL &&
287 1 : CPLGetValueType(papszTokens[5]) == CPL_VALUE_REAL &&
288 1 : CPLAtof(papszTokens[4]) >= -90 && CPLAtof(papszTokens[4]) <= 90 &&
289 2 : CPLAtof(papszTokens[5]) >= -180 && CPLAtof(papszTokens[4]) <= 180)
290 : {
291 1 : CSLDestroy(papszTokens);
292 1 : papszTokens = nullptr;
293 :
294 : static const struct
295 : {
296 : const char *pszName;
297 : OGRFieldType eType;
298 : } asGeonamesFieldDesc[] = {
299 : {"GEONAMEID", OFTString}, {"NAME", OFTString},
300 : {"ASCIINAME", OFTString}, {"ALTNAMES", OFTString},
301 : {"LATITUDE", OFTReal}, {"LONGITUDE", OFTReal},
302 : {"FEATCLASS", OFTString}, {"FEATCODE", OFTString},
303 : {"COUNTRY", OFTString}, {"CC2", OFTString},
304 : {"ADMIN1", OFTString}, {"ADMIN2", OFTString},
305 : {"ADMIN3", OFTString}, {"ADMIN4", OFTString},
306 : {"POPULATION", OFTReal}, {"ELEVATION", OFTInteger},
307 : {"GTOPO30", OFTInteger}, {"TIMEZONE", OFTString},
308 : {"MODDATE", OFTString}};
309 :
310 20 : for (int iField = 0; iField < nFieldCount; iField++)
311 : {
312 19 : OGRFieldDefn oFieldDefn(asGeonamesFieldDesc[iField].pszName,
313 38 : asGeonamesFieldDesc[iField].eType);
314 19 : poFeatureDefn->AddFieldDefn(&oFieldDefn);
315 : }
316 :
317 1 : iLatitudeField = 4;
318 1 : iLongitudeField = 5;
319 :
320 1 : nFieldCount = 0;
321 :
322 1 : bHonourStrings = false;
323 : }
324 : }
325 :
326 : // Search a csvt file for types.
327 781 : char **papszFieldTypes = nullptr;
328 781 : if (!bNew)
329 : {
330 : // Only try to read .csvt from files that have an extension
331 648 : if (!CPLGetExtensionSafe(pszFilename).empty())
332 : {
333 : std::string osCSVTFilename =
334 1296 : CPLResetExtensionSafe(pszFilename, "csvt");
335 648 : VSILFILE *fpCSVT = VSIFOpenL(osCSVTFilename.c_str(), "r");
336 648 : if (fpCSVT != nullptr)
337 : {
338 127 : m_osCSVTFilename = std::move(osCSVTFilename);
339 127 : VSIRewindL(fpCSVT);
340 : papszFieldTypes =
341 127 : CSVReadParseLine3L(fpCSVT, m_nMaxLineSize, ",",
342 : true, // bHonourStrings
343 : false, // bKeepLeadingAndClosingQuotes
344 : false, // bMergeDelimiter,
345 : true // bSkipBOM
346 : );
347 127 : VSIFCloseL(fpCSVT);
348 : }
349 : }
350 : }
351 :
352 : // Optionally auto-detect types.
353 1302 : if (!bNew && papszFieldTypes == nullptr &&
354 521 : CPLTestBool(
355 : CSLFetchNameValueDef(papszOpenOptions, "AUTODETECT_TYPE", "NO")))
356 : {
357 25 : papszFieldTypes = AutodetectFieldTypes(papszOpenOptions, nFieldCount);
358 25 : if (papszFieldTypes != nullptr)
359 : {
360 25 : bKeepSourceColumns = CPLTestBool(CSLFetchNameValueDef(
361 : papszOpenOptions, "KEEP_SOURCE_COLUMNS", "NO"));
362 : }
363 : }
364 :
365 781 : char **papszGeomPossibleNames = CSLTokenizeString2(
366 : CSLFetchNameValue(papszOpenOptions, "GEOM_POSSIBLE_NAMES"), ",", 0);
367 781 : char **papszXPossibleNames = CSLTokenizeString2(
368 : CSLFetchNameValue(papszOpenOptions, "X_POSSIBLE_NAMES"), ",", 0);
369 781 : char **papszYPossibleNames = CSLTokenizeString2(
370 : CSLFetchNameValue(papszOpenOptions, "Y_POSSIBLE_NAMES"), ",", 0);
371 781 : char **papszZPossibleNames = CSLTokenizeString2(
372 : CSLFetchNameValue(papszOpenOptions, "Z_POSSIBLE_NAMES"), ",", 0);
373 781 : bKeepGeomColumns = CPLTestBool(
374 : CSLFetchNameValueDef(papszOpenOptions, "KEEP_GEOM_COLUMNS", "YES"));
375 :
376 : // Build field definitions.
377 781 : poFeatureDefn->ReserveSpaceForFields(nFieldCount);
378 :
379 781 : constexpr int knMAX_GEOM_COLUMNS = 100;
380 781 : bool bWarnedMaxGeomFields = false;
381 :
382 781 : const int nFieldTypesCount = CSLCount(papszFieldTypes);
383 :
384 3926 : for (int iField = 0; !bIsEurostatTSV && iField < nFieldCount; iField++)
385 : {
386 3145 : char *pszFieldName = nullptr;
387 : char szFieldNameBuffer[100];
388 :
389 3145 : if (bHasFieldNames)
390 : {
391 2715 : pszFieldName = papszTokens[iField];
392 :
393 : // Trim white space.
394 2723 : while (*pszFieldName == ' ')
395 8 : pszFieldName++;
396 :
397 2723 : while (pszFieldName[0] != '\0' &&
398 2723 : pszFieldName[strlen(pszFieldName) - 1] == ' ')
399 8 : pszFieldName[strlen(pszFieldName) - 1] = '\0';
400 :
401 2715 : if (*pszFieldName == '\0')
402 0 : pszFieldName = nullptr;
403 : }
404 :
405 3145 : if (pszFieldName == nullptr)
406 : {
407 : // Re-read single column CSV files that have a trailing comma
408 : // in the header line.
409 430 : if (iField == 1 && nFieldCount == 2 && papszTokens[1][0] == '\0')
410 : {
411 0 : nCSVFieldCount = 1;
412 0 : nFieldCount = 1;
413 0 : break;
414 : }
415 430 : pszFieldName = szFieldNameBuffer;
416 430 : snprintf(szFieldNameBuffer, sizeof(szFieldNameBuffer), "field_%d",
417 : iField + 1);
418 : }
419 :
420 3145 : OGRFieldDefn oField(pszFieldName, OFTString);
421 3145 : if (papszFieldTypes != nullptr && iField < nFieldTypesCount)
422 : {
423 929 : if (EQUAL(papszFieldTypes[iField], "WKT"))
424 : {
425 14 : if (bKeepGeomColumns)
426 14 : poFeatureDefn->AddFieldDefn(&oField);
427 :
428 14 : if (poFeatureDefn->GetGeomFieldCount() == knMAX_GEOM_COLUMNS)
429 : {
430 0 : if (!bWarnedMaxGeomFields)
431 : {
432 0 : CPLError(CE_Warning, CPLE_NotSupported,
433 : "A maximum number of %d geometry fields is "
434 : "supported. "
435 : "Only the first ones are taken into account.",
436 : knMAX_GEOM_COLUMNS);
437 0 : bWarnedMaxGeomFields = true;
438 : }
439 0 : continue;
440 : }
441 :
442 14 : eGeometryFormat = OGR_CSV_GEOM_AS_WKT;
443 14 : panGeomFieldIndex[iField] = poFeatureDefn->GetGeomFieldCount();
444 28 : std::string osGeomColName;
445 14 : if (bKeepGeomColumns)
446 14 : osGeomColName += "geom_";
447 14 : osGeomColName += oField.GetNameRef();
448 : OGRGeomFieldDefn oGeomFieldDefn(osGeomColName.c_str(),
449 28 : wkbUnknown);
450 14 : poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
451 14 : continue;
452 : }
453 915 : else if (EQUAL(papszFieldTypes[iField], "CoordX") ||
454 911 : EQUAL(papszFieldTypes[iField], "Point(X)"))
455 : {
456 4 : oField.SetType(OFTReal);
457 4 : iLongitudeField = iField;
458 4 : osXField = oField.GetNameRef();
459 4 : if (bKeepGeomColumns)
460 4 : poFeatureDefn->AddFieldDefn(&oField);
461 4 : continue;
462 : }
463 911 : else if (EQUAL(papszFieldTypes[iField], "CoordY") ||
464 907 : EQUAL(papszFieldTypes[iField], "Point(Y)"))
465 : {
466 4 : oField.SetType(OFTReal);
467 4 : iLatitudeField = iField;
468 4 : osYField = oField.GetNameRef();
469 4 : if (bKeepGeomColumns)
470 4 : poFeatureDefn->AddFieldDefn(&oField);
471 4 : continue;
472 : }
473 907 : else if (EQUAL(papszFieldTypes[iField], "CoordZ") ||
474 907 : EQUAL(papszFieldTypes[iField], "Point(Z)"))
475 : {
476 0 : oField.SetType(OFTReal);
477 0 : iZField = iField;
478 0 : osZField = oField.GetNameRef();
479 0 : if (bKeepGeomColumns)
480 0 : poFeatureDefn->AddFieldDefn(&oField);
481 0 : continue;
482 : }
483 907 : else if (EQUAL(papszFieldTypes[iField], "Integer(Boolean)"))
484 : {
485 13 : oField.SetType(OFTInteger);
486 13 : oField.SetSubType(OFSTBoolean);
487 13 : oField.SetWidth(1);
488 : }
489 894 : else if (EQUAL(papszFieldTypes[iField], "Integer(Int16)"))
490 : {
491 1 : oField.SetType(OFTInteger);
492 1 : oField.SetSubType(OFSTInt16);
493 : }
494 893 : else if (EQUAL(papszFieldTypes[iField], "Real(Float32)"))
495 : {
496 1 : oField.SetType(OFTReal);
497 1 : oField.SetSubType(OFSTFloat32);
498 : }
499 : else
500 : {
501 892 : char *pszLeftParenthesis = strchr(papszFieldTypes[iField], '(');
502 892 : if (pszLeftParenthesis &&
503 143 : pszLeftParenthesis != papszFieldTypes[iField] &&
504 143 : pszLeftParenthesis[1] >= '0' &&
505 143 : pszLeftParenthesis[1] <= '9')
506 : {
507 143 : char *pszDot = strchr(pszLeftParenthesis, '.');
508 143 : if (pszDot)
509 41 : *pszDot = 0;
510 143 : *pszLeftParenthesis = 0;
511 :
512 143 : if (pszLeftParenthesis[-1] == ' ')
513 119 : pszLeftParenthesis[-1] = 0;
514 :
515 143 : const int nWidth = atoi(pszLeftParenthesis + 1);
516 143 : const int nPrecision = pszDot ? atoi(pszDot + 1) : 0;
517 :
518 143 : oField.SetWidth(nWidth);
519 143 : oField.SetPrecision(nPrecision);
520 : }
521 :
522 892 : if (EQUAL(papszFieldTypes[iField], "Integer"))
523 85 : oField.SetType(OFTInteger);
524 807 : else if (EQUAL(papszFieldTypes[iField], "Integer64"))
525 9 : oField.SetType(OFTInteger64);
526 798 : else if (EQUAL(papszFieldTypes[iField], "Real"))
527 221 : oField.SetType(OFTReal);
528 577 : else if (EQUAL(papszFieldTypes[iField], "String"))
529 462 : oField.SetType(OFTString);
530 115 : else if (EQUAL(papszFieldTypes[iField], "Date"))
531 24 : oField.SetType(OFTDate);
532 91 : else if (EQUAL(papszFieldTypes[iField], "Time"))
533 21 : oField.SetType(OFTTime);
534 70 : else if (EQUAL(papszFieldTypes[iField], "DateTime"))
535 61 : oField.SetType(OFTDateTime);
536 9 : else if (EQUAL(papszFieldTypes[iField], "JSonStringList"))
537 3 : oField.SetType(OFTStringList);
538 6 : else if (EQUAL(papszFieldTypes[iField], "JSonIntegerList"))
539 2 : oField.SetType(OFTIntegerList);
540 4 : else if (EQUAL(papszFieldTypes[iField], "JSonInteger64List"))
541 2 : oField.SetType(OFTInteger64List);
542 2 : else if (EQUAL(papszFieldTypes[iField], "JSonRealList"))
543 2 : oField.SetType(OFTRealList);
544 : else
545 0 : CPLError(CE_Warning, CPLE_NotSupported, "Unknown type : %s",
546 0 : papszFieldTypes[iField]);
547 : }
548 : }
549 :
550 3123 : if (Matches(oField.GetNameRef(), papszZPossibleNames))
551 : {
552 4 : oField.SetType(OFTReal);
553 4 : iZField = iField;
554 4 : osZField = oField.GetNameRef();
555 4 : if (!bKeepGeomColumns)
556 2 : continue;
557 : }
558 3119 : else if ((iNfdcLatitudeS != -1 && iNfdcLongitudeS != -1) ||
559 3119 : (iLatitudeField != -1 && iLongitudeField != -1))
560 : {
561 : // Do nothing.
562 : }
563 3106 : else if ((EQUAL(oField.GetNameRef(), "WKT") ||
564 3596 : STARTS_WITH_CI(oField.GetNameRef(), "_WKT")) &&
565 490 : oField.GetType() == OFTString)
566 : {
567 490 : if (poFeatureDefn->GetGeomFieldCount() == knMAX_GEOM_COLUMNS)
568 : {
569 1 : if (!bWarnedMaxGeomFields)
570 : {
571 1 : CPLError(
572 : CE_Warning, CPLE_NotSupported,
573 : "A maximum number of %d geometry fields is supported. "
574 : "Only the first ones are taken into account.",
575 : knMAX_GEOM_COLUMNS);
576 1 : bWarnedMaxGeomFields = true;
577 : }
578 : }
579 : else
580 : {
581 489 : eGeometryFormat = OGR_CSV_GEOM_AS_WKT;
582 :
583 489 : panGeomFieldIndex[iField] = poFeatureDefn->GetGeomFieldCount();
584 : OGRGeomFieldDefn oGeomFieldDefn(
585 489 : EQUAL(pszFieldName, "WKT")
586 : ? ""
587 247 : : CPLSPrintf("geom_%s", pszFieldName),
588 736 : wkbUnknown);
589 :
590 : // Useful hack for RFC 41 testing.
591 489 : const char *pszEPSG = strstr(pszFieldName, "_EPSG_");
592 489 : if (pszEPSG != nullptr)
593 : {
594 242 : const int nEPSGCode = atoi(pszEPSG + strlen("_EPSG_"));
595 242 : OGRSpatialReference *poSRS = new OGRSpatialReference();
596 242 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
597 242 : poSRS->importFromEPSG(nEPSGCode);
598 242 : oGeomFieldDefn.SetSpatialRef(poSRS);
599 242 : poSRS->Release();
600 : }
601 :
602 489 : if (strstr(pszFieldName, "_POINT"))
603 102 : oGeomFieldDefn.SetType(wkbPoint);
604 387 : else if (strstr(pszFieldName, "_LINESTRING"))
605 34 : oGeomFieldDefn.SetType(wkbLineString);
606 353 : else if (strstr(pszFieldName, "_POLYGON"))
607 68 : oGeomFieldDefn.SetType(wkbPolygon);
608 285 : else if (strstr(pszFieldName, "_MULTIPOINT"))
609 0 : oGeomFieldDefn.SetType(wkbMultiPoint);
610 285 : else if (strstr(pszFieldName, "_MULTILINESTRING"))
611 0 : oGeomFieldDefn.SetType(wkbMultiLineString);
612 285 : else if (strstr(pszFieldName, "_MULTIPOLYGON"))
613 0 : oGeomFieldDefn.SetType(wkbMultiPolygon);
614 285 : else if (strstr(pszFieldName, "_CIRCULARSTRING"))
615 0 : oGeomFieldDefn.SetType(wkbCircularString);
616 285 : else if (strstr(pszFieldName, "_COMPOUNDCURVE"))
617 0 : oGeomFieldDefn.SetType(wkbCompoundCurve);
618 285 : else if (strstr(pszFieldName, "_CURVEPOLYGON"))
619 4 : oGeomFieldDefn.SetType(wkbCurvePolygon);
620 281 : else if (strstr(pszFieldName, "_CURVE"))
621 0 : oGeomFieldDefn.SetType(wkbCurve);
622 281 : else if (strstr(pszFieldName, "_SURFACE"))
623 0 : oGeomFieldDefn.SetType(wkbSurface);
624 281 : else if (strstr(pszFieldName, "_MULTICURVE"))
625 0 : oGeomFieldDefn.SetType(wkbMultiCurve);
626 281 : else if (strstr(pszFieldName, "_MULTISURFACE"))
627 0 : oGeomFieldDefn.SetType(wkbMultiSurface);
628 281 : else if (strstr(pszFieldName, "_POLYHEDRALSURFACE"))
629 0 : oGeomFieldDefn.SetType(wkbPolyhedralSurface);
630 281 : else if (strstr(pszFieldName, "_TIN"))
631 0 : oGeomFieldDefn.SetType(wkbTIN);
632 281 : else if (strstr(pszFieldName, "_TRIANGLE"))
633 0 : oGeomFieldDefn.SetType(wkbTriangle);
634 :
635 489 : poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
636 489 : if (!bKeepGeomColumns)
637 0 : continue;
638 : }
639 : }
640 2616 : else if (Matches(oField.GetNameRef(), papszGeomPossibleNames))
641 : {
642 12 : eGeometryFormat = OGR_CSV_GEOM_AS_SOME_GEOM_FORMAT;
643 12 : panGeomFieldIndex[iField] = poFeatureDefn->GetGeomFieldCount();
644 12 : OGRGeomFieldDefn oGeomFieldDefn(oField.GetNameRef(), wkbUnknown);
645 12 : poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
646 12 : if (!bKeepGeomColumns)
647 7 : continue;
648 : }
649 2617 : else if (Matches(oField.GetNameRef(), papszXPossibleNames) &&
650 13 : poFeatureDefn->GetGeomFieldCount() == 0)
651 : {
652 12 : oField.SetType(OFTReal);
653 12 : iLongitudeField = iField;
654 12 : osXField = oField.GetNameRef();
655 12 : if (!bKeepGeomColumns)
656 6 : continue;
657 : }
658 2605 : else if (Matches(oField.GetNameRef(), papszYPossibleNames) &&
659 13 : poFeatureDefn->GetGeomFieldCount() == 0)
660 : {
661 12 : oField.SetType(OFTReal);
662 12 : iLatitudeField = iField;
663 12 : osYField = oField.GetNameRef();
664 12 : if (!bKeepGeomColumns)
665 6 : continue;
666 : }
667 :
668 : // TODO(schwehr): URL broken.
669 : // http://www.faa.gov/airports/airport_safety/airportdata_5010/menu/index.cfm
670 : // specific
671 2580 : else if (pszNfdcGeomField != nullptr &&
672 0 : EQUALN(oField.GetNameRef(), pszNfdcGeomField,
673 0 : strlen(pszNfdcGeomField)) &&
674 0 : EQUAL(oField.GetNameRef() + strlen(pszNfdcGeomField),
675 2580 : "LatitudeS") &&
676 0 : poFeatureDefn->GetGeomFieldCount() == 0)
677 : {
678 0 : iNfdcLatitudeS = iField;
679 0 : if (!bKeepGeomColumns)
680 0 : continue;
681 : }
682 2580 : else if (pszNfdcGeomField != nullptr &&
683 0 : EQUALN(oField.GetNameRef(), pszNfdcGeomField,
684 0 : strlen(pszNfdcGeomField)) &&
685 0 : EQUAL(oField.GetNameRef() + strlen(pszNfdcGeomField),
686 2580 : "LongitudeS") &&
687 0 : poFeatureDefn->GetGeomFieldCount() == 0)
688 : {
689 0 : iNfdcLongitudeS = iField;
690 0 : if (!bKeepGeomColumns)
691 0 : continue;
692 : }
693 : // GNIS specific.
694 2580 : else if (pszGeonamesGeomFieldPrefix != nullptr &&
695 0 : EQUALN(oField.GetNameRef(), pszGeonamesGeomFieldPrefix,
696 0 : strlen(pszGeonamesGeomFieldPrefix)) &&
697 0 : (EQUAL(oField.GetNameRef() +
698 : strlen(pszGeonamesGeomFieldPrefix),
699 0 : "_LAT_DEC") ||
700 0 : EQUAL(oField.GetNameRef() +
701 : strlen(pszGeonamesGeomFieldPrefix),
702 0 : "_LATITUDE_DEC") ||
703 0 : EQUAL(oField.GetNameRef() +
704 : strlen(pszGeonamesGeomFieldPrefix),
705 2580 : "_LATITUDE")) &&
706 0 : poFeatureDefn->GetGeomFieldCount() == 0)
707 : {
708 0 : m_bIsGNIS = true;
709 0 : oField.SetType(OFTReal);
710 0 : iLatitudeField = iField;
711 0 : osYField = oField.GetNameRef();
712 0 : if (!bKeepGeomColumns)
713 0 : continue;
714 : }
715 2580 : else if (pszGeonamesGeomFieldPrefix != nullptr &&
716 0 : EQUALN(oField.GetNameRef(), pszGeonamesGeomFieldPrefix,
717 0 : strlen(pszGeonamesGeomFieldPrefix)) &&
718 0 : (EQUAL(oField.GetNameRef() +
719 : strlen(pszGeonamesGeomFieldPrefix),
720 0 : "_LONG_DEC") ||
721 0 : EQUAL(oField.GetNameRef() +
722 : strlen(pszGeonamesGeomFieldPrefix),
723 0 : "_LONGITUDE_DEC") ||
724 0 : EQUAL(oField.GetNameRef() +
725 : strlen(pszGeonamesGeomFieldPrefix),
726 2580 : "_LONGITUDE")) &&
727 0 : poFeatureDefn->GetGeomFieldCount() == 0)
728 : {
729 0 : m_bIsGNIS = true;
730 0 : oField.SetType(OFTReal);
731 0 : iLongitudeField = iField;
732 0 : osXField = oField.GetNameRef();
733 0 : if (!bKeepGeomColumns)
734 0 : continue;
735 : }
736 :
737 3102 : poFeatureDefn->AddFieldDefn(&oField);
738 :
739 3102 : if (bKeepSourceColumns && oField.GetType() != OFTString)
740 : {
741 : OGRFieldDefn oFieldOriginal(
742 22 : CPLSPrintf("%s_original", oField.GetNameRef()), OFTString);
743 11 : poFeatureDefn->AddFieldDefn(&oFieldOriginal);
744 : }
745 : }
746 :
747 781 : if (iNfdcLatitudeS != -1 && iNfdcLongitudeS != -1)
748 : {
749 0 : bHonourStrings = false;
750 0 : if (poFeatureDefn->GetGeomFieldCount() == 0)
751 : {
752 0 : poFeatureDefn->SetGeomType(wkbPoint);
753 : }
754 : else
755 : {
756 0 : iNfdcLatitudeS = -1;
757 0 : iNfdcLongitudeS = -1;
758 0 : iLatitudeField = -1;
759 0 : iLongitudeField = -1;
760 : }
761 : }
762 781 : else if (iLatitudeField != -1 && iLongitudeField != -1)
763 : {
764 17 : if (poFeatureDefn->GetGeomFieldCount() == 0)
765 : {
766 17 : poFeatureDefn->SetGeomType(iZField >= 0 ? wkbPoint25D : wkbPoint);
767 : }
768 : else
769 : {
770 0 : iNfdcLatitudeS = -1;
771 0 : iNfdcLongitudeS = -1;
772 0 : iLatitudeField = -1;
773 0 : iLongitudeField = -1;
774 : }
775 : }
776 :
777 1054 : if (poFeatureDefn->GetGeomFieldCount() > 0 &&
778 273 : poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef() == nullptr)
779 : {
780 : VSILFILE *fpPRJ =
781 184 : VSIFOpenL(CPLResetExtensionSafe(pszFilename, "prj").c_str(), "rb");
782 184 : if (fpPRJ != nullptr)
783 : {
784 2 : GByte *pabyRet = nullptr;
785 2 : if (VSIIngestFile(fpPRJ, nullptr, &pabyRet, nullptr, 1000000))
786 : {
787 2 : OGRSpatialReference *poSRS = new OGRSpatialReference();
788 2 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
789 2 : if (poSRS->SetFromUserInput(
790 : (const char *)pabyRet,
791 : OGRSpatialReference::
792 2 : SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
793 : OGRERR_NONE)
794 : {
795 2 : poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
796 : }
797 2 : poSRS->Release();
798 : }
799 2 : CPLFree(pabyRet);
800 2 : VSIFCloseL(fpPRJ);
801 : }
802 : }
803 :
804 781 : CSLDestroy(papszGeomPossibleNames);
805 781 : CSLDestroy(papszXPossibleNames);
806 781 : CSLDestroy(papszYPossibleNames);
807 781 : CSLDestroy(papszZPossibleNames);
808 :
809 : // Build field definitions for Eurostat TSV files.
810 :
811 1562 : CPLString osSeqDim;
812 785 : for (int iField = 0; bIsEurostatTSV && iField < nFieldCount; iField++)
813 : {
814 4 : if (iField == 0)
815 : {
816 1 : char **papszDims = CSLTokenizeString2(papszTokens[0], ",\\", 0);
817 1 : nEurostatDims = CSLCount(papszDims) - 1;
818 3 : for (int iSubField = 0; iSubField < nEurostatDims; iSubField++)
819 : {
820 4 : OGRFieldDefn oField(papszDims[iSubField], OFTString);
821 2 : poFeatureDefn->AddFieldDefn(&oField);
822 : }
823 :
824 1 : if (nEurostatDims >= 0)
825 1 : osSeqDim = papszDims[nEurostatDims];
826 : else
827 0 : CPLError(CE_Warning, CPLE_AppDefined, "Invalid nEurostatDims");
828 :
829 1 : CSLDestroy(papszDims);
830 : }
831 : else
832 : {
833 3 : if (papszTokens[iField][0] != '\0' &&
834 3 : papszTokens[iField][strlen(papszTokens[iField]) - 1] == ' ')
835 3 : papszTokens[iField][strlen(papszTokens[iField]) - 1] = '\0';
836 :
837 : OGRFieldDefn oField(
838 3 : CPLSPrintf("%s_%s", osSeqDim.c_str(), papszTokens[iField]),
839 6 : OFTReal);
840 3 : poFeatureDefn->AddFieldDefn(&oField);
841 :
842 : OGRFieldDefn oField2(
843 3 : CPLSPrintf("%s_%s_flag", osSeqDim.c_str(), papszTokens[iField]),
844 6 : OFTString);
845 3 : poFeatureDefn->AddFieldDefn(&oField2);
846 : }
847 : }
848 :
849 781 : CSLDestroy(papszTokens);
850 781 : CSLDestroy(papszFieldTypes);
851 781 : }
852 :
853 : /************************************************************************/
854 : /* GetFileList() */
855 : /************************************************************************/
856 :
857 29 : std::vector<std::string> OGRCSVLayer::GetFileList()
858 : {
859 29 : std::vector<std::string> ret;
860 29 : ret.emplace_back(pszFilename);
861 29 : if (!m_osCSVTFilename.empty())
862 11 : ret.emplace_back(m_osCSVTFilename);
863 29 : return ret;
864 : }
865 :
866 : /************************************************************************/
867 : /* OGRCSVIsTrue() */
868 : /************************************************************************/
869 :
870 193 : static bool OGRCSVIsTrue(const char *pszStr)
871 : {
872 190 : return EQUAL(pszStr, "t") || EQUAL(pszStr, "true") || EQUAL(pszStr, "y") ||
873 383 : EQUAL(pszStr, "yes") || EQUAL(pszStr, "on");
874 : }
875 :
876 : /************************************************************************/
877 : /* OGRCSVIsFalse() */
878 : /************************************************************************/
879 :
880 172 : static bool OGRCSVIsFalse(const char *pszStr)
881 : {
882 169 : return EQUAL(pszStr, "f") || EQUAL(pszStr, "false") || EQUAL(pszStr, "n") ||
883 341 : EQUAL(pszStr, "no") || EQUAL(pszStr, "off");
884 : }
885 :
886 : /************************************************************************/
887 : /* AutodetectFieldTypes() */
888 : /************************************************************************/
889 :
890 25 : char **OGRCSVLayer::AutodetectFieldTypes(CSLConstList papszOpenOptions,
891 : int nFieldCount)
892 : {
893 : const bool bStreaming =
894 50 : STARTS_WITH(pszFilename, "/vsistdin") ||
895 : // config option for testing purposes only
896 25 : CPLTestBool(CPLGetConfigOption("OGR_CSV_SIMULATE_VSISTDIN", "NO"));
897 25 : constexpr int STREAMING_LIMIT = 1000 * 1000;
898 : // Use 1 000 000 as default maximum distance to be compatible with
899 : // /vsistdin/ caching.
900 25 : vsi_l_offset nBytes = static_cast<vsi_l_offset>(CPLAtoGIntBig(
901 : CSLFetchNameValueDef(papszOpenOptions, "AUTODETECT_SIZE_LIMIT",
902 25 : CPLSPrintf("%d", STREAMING_LIMIT))));
903 25 : if (nBytes == 0)
904 2 : nBytes = static_cast<vsi_l_offset>(-1); // unlimited size
905 25 : if (bStreaming && (nBytes == 0 || nBytes > STREAMING_LIMIT))
906 : {
907 1 : CPLError(CE_Warning, CPLE_AppDefined,
908 : "Limiting AUTODETECT_SIZE_LIMIT to %d for /vsistdin/",
909 : STREAMING_LIMIT);
910 1 : nBytes = STREAMING_LIMIT;
911 : }
912 :
913 25 : ResetReading();
914 :
915 : const char *pszAutodetectWidth =
916 25 : CSLFetchNameValueDef(papszOpenOptions, "AUTODETECT_WIDTH", "NO");
917 :
918 25 : const bool bAutodetectWidthForIntOrReal = EQUAL(pszAutodetectWidth, "YES");
919 41 : const bool bAutodetectWidth = bAutodetectWidthForIntOrReal ||
920 16 : EQUAL(pszAutodetectWidth, "STRING_ONLY");
921 :
922 25 : const bool bQuotedFieldAsString = CPLTestBool(CSLFetchNameValueDef(
923 : papszOpenOptions, "QUOTED_FIELDS_AS_STRING", "NO"));
924 :
925 : // This will be returned as the result.
926 25 : char **papszFieldTypes = nullptr;
927 :
928 25 : char *pszData = nullptr;
929 25 : VSILFILE *fp = fpCSV;
930 50 : std::string osTmpMemFile;
931 25 : size_t nRead = 0;
932 25 : int nRequested = 0;
933 25 : if (bStreaming)
934 : {
935 : // The above ResetReading() will skip the header line,
936 : // so VSIFTellL(fpCSV) != 0
937 2 : nRequested =
938 2 : static_cast<int>(nBytes) - static_cast<int>(VSIFTellL(fpCSV));
939 2 : if (nRequested <= 0)
940 0 : return nullptr;
941 2 : pszData = static_cast<char *>(VSI_MALLOC_VERBOSE(nRequested + 1));
942 2 : if (pszData == nullptr)
943 0 : return nullptr;
944 2 : nRead = VSIFReadL(pszData, 1, nRequested, fpCSV);
945 2 : pszData[nRead] = 0;
946 :
947 2 : osTmpMemFile = VSIMemGenerateHiddenFilename("temp.csv");
948 2 : fp = VSIFileFromMemBuffer(osTmpMemFile.c_str(),
949 : reinterpret_cast<GByte *>(pszData), nRead,
950 : FALSE);
951 : }
952 :
953 50 : std::vector<OGRFieldType> aeFieldType(nFieldCount);
954 50 : std::vector<int> abFieldBoolean(nFieldCount);
955 50 : std::vector<int> abFieldSet(nFieldCount);
956 50 : std::vector<int> abFinalTypeStringSet(nFieldCount);
957 50 : std::vector<int> anFieldWidth(nFieldCount);
958 25 : std::vector<int> anFieldPrecision(nFieldCount);
959 25 : int nStringFieldCount = 0;
960 :
961 95 : while (!fp->Eof() && !fp->Error())
962 : {
963 : char **papszTokens =
964 170 : CSVReadParseLine3L(fp, m_nMaxLineSize, szDelimiter,
965 : true, // bHonourStrings
966 85 : bQuotedFieldAsString, bMergeDelimiter,
967 : true // bSkipBOM
968 : );
969 : // Can happen if we just reach EOF while trying to read new bytes.
970 85 : if (papszTokens == nullptr)
971 0 : break;
972 :
973 85 : if (bStreaming)
974 : {
975 : // Ignore last line if it is truncated.
976 13 : if (fp->Eof() && nRead == static_cast<size_t>(nRequested) &&
977 13 : pszData[nRead - 1] != 13 && pszData[nRead - 1] != 10)
978 : {
979 1 : CSLDestroy(papszTokens);
980 1 : break;
981 : }
982 : }
983 74 : else if (VSIFTellL(fp) > nBytes)
984 : {
985 13 : CSLDestroy(papszTokens);
986 13 : break;
987 : }
988 :
989 1192 : for (int iField = 0;
990 1192 : iField < nFieldCount && papszTokens[iField] != nullptr; iField++)
991 : {
992 1121 : if (papszTokens[iField][0] == 0)
993 482 : continue;
994 695 : if (abFinalTypeStringSet[iField] && !bAutodetectWidth)
995 56 : continue;
996 639 : if (szDelimiter[0] == ';')
997 : {
998 0 : char *chComma = strchr(papszTokens[iField], ',');
999 0 : if (chComma)
1000 0 : *chComma = '.';
1001 : }
1002 639 : const CPLValueType eType = CPLGetValueType(papszTokens[iField]);
1003 :
1004 639 : if (bAutodetectWidth)
1005 : {
1006 324 : int nFieldWidth = static_cast<int>(strlen(papszTokens[iField]));
1007 324 : if (papszTokens[iField][0] == '"' &&
1008 6 : papszTokens[iField][nFieldWidth - 1] == '"')
1009 : {
1010 6 : nFieldWidth -= 2;
1011 : }
1012 324 : int nFieldPrecision = 0;
1013 324 : if (eType == CPL_VALUE_REAL && bAutodetectWidthForIntOrReal)
1014 : {
1015 40 : const char *pszDot = strchr(papszTokens[iField], '.');
1016 40 : if (pszDot != nullptr)
1017 40 : nFieldPrecision = static_cast<int>(strlen(pszDot + 1));
1018 : }
1019 :
1020 324 : if (nFieldWidth > anFieldWidth[iField])
1021 205 : anFieldWidth[iField] = nFieldWidth;
1022 324 : if (nFieldPrecision > anFieldPrecision[iField])
1023 32 : anFieldPrecision[iField] = nFieldPrecision;
1024 : }
1025 :
1026 : OGRFieldType eOGRFieldType;
1027 639 : bool bIsBoolean = false;
1028 639 : if (eType == CPL_VALUE_INTEGER)
1029 : {
1030 131 : GIntBig nVal = CPLAtoGIntBig(papszTokens[iField]);
1031 131 : if (!CPL_INT64_FITS_ON_INT32(nVal))
1032 6 : eOGRFieldType = OFTInteger64;
1033 : else
1034 125 : eOGRFieldType = OFTInteger;
1035 : }
1036 508 : else if (eType == CPL_VALUE_REAL ||
1037 416 : EQUAL(papszTokens[iField], "inf") ||
1038 415 : EQUAL(papszTokens[iField], "-inf") ||
1039 414 : EQUAL(papszTokens[iField], "nan"))
1040 : {
1041 95 : eOGRFieldType = OFTReal;
1042 : }
1043 413 : else if (abFieldSet[iField] && aeFieldType[iField] == OFTString)
1044 : {
1045 38 : eOGRFieldType = OFTString;
1046 38 : if (abFieldBoolean[iField])
1047 : {
1048 13 : abFieldBoolean[iField] =
1049 21 : OGRCSVIsTrue(papszTokens[iField]) ||
1050 8 : OGRCSVIsFalse(papszTokens[iField]);
1051 : }
1052 : }
1053 : else
1054 : {
1055 : OGRField sWrkField;
1056 375 : CPLPushErrorHandler(CPLQuietErrorHandler);
1057 375 : const bool bSuccess = CPL_TO_BOOL(
1058 375 : OGRParseDate(papszTokens[iField], &sWrkField, 0));
1059 375 : CPLPopErrorHandler();
1060 375 : CPLErrorReset();
1061 375 : if (bSuccess)
1062 : {
1063 209 : const bool bHasDate =
1064 418 : strchr(papszTokens[iField], '/') != nullptr ||
1065 209 : strchr(papszTokens[iField], '-') != nullptr;
1066 209 : const bool bHasTime =
1067 209 : strchr(papszTokens[iField], ':') != nullptr;
1068 209 : if (bHasDate && bHasTime)
1069 82 : eOGRFieldType = OFTDateTime;
1070 127 : else if (bHasDate)
1071 79 : eOGRFieldType = OFTDate;
1072 : else
1073 48 : eOGRFieldType = OFTTime;
1074 : }
1075 : else
1076 : {
1077 166 : eOGRFieldType = OFTString;
1078 324 : bIsBoolean = OGRCSVIsTrue(papszTokens[iField]) ||
1079 158 : OGRCSVIsFalse(papszTokens[iField]);
1080 : }
1081 : }
1082 :
1083 166 : const auto SetFinalStringType = [&abFinalTypeStringSet,
1084 : &aeFieldType, &nStringFieldCount,
1085 664 : iField]()
1086 : {
1087 166 : if (!abFinalTypeStringSet[iField])
1088 : {
1089 166 : aeFieldType[iField] = OFTString;
1090 166 : abFinalTypeStringSet[iField] = true;
1091 166 : nStringFieldCount++;
1092 : }
1093 805 : };
1094 :
1095 639 : if (!abFieldSet[iField])
1096 : {
1097 356 : aeFieldType[iField] = eOGRFieldType;
1098 356 : abFieldSet[iField] = TRUE;
1099 356 : abFieldBoolean[iField] = bIsBoolean;
1100 356 : if (eOGRFieldType == OFTString && !bIsBoolean)
1101 : {
1102 50 : SetFinalStringType();
1103 : }
1104 : }
1105 283 : else if (aeFieldType[iField] != eOGRFieldType)
1106 : {
1107 : // Promotion rules.
1108 164 : if (aeFieldType[iField] == OFTInteger)
1109 : {
1110 39 : if (eOGRFieldType == OFTInteger64 ||
1111 : eOGRFieldType == OFTReal)
1112 16 : aeFieldType[iField] = eOGRFieldType;
1113 : else
1114 : {
1115 23 : SetFinalStringType();
1116 : }
1117 : }
1118 125 : else if (aeFieldType[iField] == OFTInteger64)
1119 : {
1120 2 : if (eOGRFieldType == OFTReal)
1121 1 : aeFieldType[iField] = eOGRFieldType;
1122 1 : else if (eOGRFieldType != OFTInteger)
1123 : {
1124 0 : SetFinalStringType();
1125 : }
1126 : }
1127 123 : else if (aeFieldType[iField] == OFTReal)
1128 : {
1129 35 : if (eOGRFieldType != OFTInteger &&
1130 : eOGRFieldType != OFTInteger64)
1131 : {
1132 20 : SetFinalStringType();
1133 : }
1134 : }
1135 88 : else if (aeFieldType[iField] == OFTDate)
1136 : {
1137 31 : if (eOGRFieldType == OFTDateTime)
1138 14 : aeFieldType[iField] = OFTDateTime;
1139 : else
1140 : {
1141 17 : SetFinalStringType();
1142 : }
1143 : }
1144 57 : else if (aeFieldType[iField] == OFTDateTime)
1145 : {
1146 40 : if (eOGRFieldType != OFTDate)
1147 : {
1148 26 : SetFinalStringType();
1149 : }
1150 : }
1151 17 : else if (aeFieldType[iField] == OFTTime)
1152 : {
1153 17 : SetFinalStringType();
1154 : }
1155 : }
1156 213 : else if (!abFinalTypeStringSet[iField] &&
1157 213 : eOGRFieldType == OFTString && !bIsBoolean)
1158 : {
1159 13 : SetFinalStringType();
1160 : }
1161 : }
1162 :
1163 71 : CSLDestroy(papszTokens);
1164 :
1165 : // If all fields are String and we don't need to compute width,
1166 : // just stop auto-detection now.
1167 71 : if (nStringFieldCount == nFieldCount && !bAutodetectWidth)
1168 : {
1169 1 : CPLDebugOnly("CSV",
1170 : "AutodetectFieldTypes() stopped after "
1171 : "reading " CPL_FRMT_GUIB " bytes",
1172 : static_cast<GUIntBig>(VSIFTellL(fp)));
1173 1 : break;
1174 : }
1175 : }
1176 :
1177 : papszFieldTypes =
1178 25 : static_cast<char **>(CPLCalloc(nFieldCount + 1, sizeof(char *)));
1179 402 : for (int iField = 0; iField < nFieldCount; iField++)
1180 : {
1181 377 : CPLString osFieldType;
1182 377 : if (!abFieldSet[iField])
1183 21 : osFieldType = "String";
1184 356 : else if (aeFieldType[iField] == OFTInteger)
1185 42 : osFieldType = "Integer";
1186 314 : else if (aeFieldType[iField] == OFTInteger64)
1187 3 : osFieldType = "Integer64";
1188 311 : else if (aeFieldType[iField] == OFTReal)
1189 52 : osFieldType = "Real";
1190 259 : else if (aeFieldType[iField] == OFTDateTime)
1191 56 : osFieldType = "DateTime";
1192 203 : else if (aeFieldType[iField] == OFTDate)
1193 20 : osFieldType = "Date";
1194 183 : else if (aeFieldType[iField] == OFTTime)
1195 17 : osFieldType = "Time";
1196 166 : else if (aeFieldType[iField] == OFTStringList)
1197 0 : osFieldType = "JSonStringList";
1198 166 : else if (aeFieldType[iField] == OFTIntegerList)
1199 0 : osFieldType = "JSonIntegerList";
1200 166 : else if (aeFieldType[iField] == OFTInteger64List)
1201 0 : osFieldType = "JSonInteger64List";
1202 166 : else if (aeFieldType[iField] == OFTRealList)
1203 0 : osFieldType = "JSonRealList";
1204 166 : else if (abFieldBoolean[iField])
1205 10 : osFieldType = "Integer(Boolean)";
1206 : else
1207 156 : osFieldType = "String";
1208 :
1209 377 : if (!abFieldBoolean[iField])
1210 : {
1211 541 : if (anFieldWidth[iField] > 0 &&
1212 174 : (aeFieldType[iField] == OFTString ||
1213 95 : (bAutodetectWidthForIntOrReal &&
1214 95 : (aeFieldType[iField] == OFTInteger ||
1215 72 : aeFieldType[iField] == OFTInteger64))))
1216 : {
1217 91 : osFieldType += CPLSPrintf(" (%d)", anFieldWidth[iField]);
1218 : }
1219 348 : else if (anFieldWidth[iField] > 0 && bAutodetectWidthForIntOrReal &&
1220 72 : aeFieldType[iField] == OFTReal)
1221 : {
1222 24 : osFieldType += CPLSPrintf(" (%d.%d)", anFieldWidth[iField],
1223 24 : anFieldPrecision[iField]);
1224 : }
1225 : }
1226 :
1227 377 : papszFieldTypes[iField] = CPLStrdup(osFieldType);
1228 : }
1229 :
1230 25 : if (bStreaming)
1231 : {
1232 2 : VSIFCloseL(fp);
1233 2 : VSIUnlink(osTmpMemFile.c_str());
1234 2 : VSIFree(pszData);
1235 : }
1236 :
1237 25 : ResetReading();
1238 :
1239 25 : return papszFieldTypes;
1240 : }
1241 :
1242 : /************************************************************************/
1243 : /* ~OGRCSVLayer() */
1244 : /************************************************************************/
1245 :
1246 1560 : OGRCSVLayer::~OGRCSVLayer()
1247 :
1248 : {
1249 780 : if (m_nFeaturesRead > 0)
1250 : {
1251 958 : CPLDebug("CSV", "%d features read on layer '%s'.",
1252 479 : static_cast<int>(m_nFeaturesRead), poFeatureDefn->GetName());
1253 : }
1254 :
1255 : // Make sure the header file is written even if no features are written.
1256 780 : if (bNew && bInWriteMode)
1257 27 : WriteHeader();
1258 :
1259 780 : CPLFree(panGeomFieldIndex);
1260 :
1261 780 : poFeatureDefn->Release();
1262 780 : CPLFree(pszFilename);
1263 :
1264 780 : if (fpCSV)
1265 780 : VSIFCloseL(fpCSV);
1266 1560 : }
1267 :
1268 : /************************************************************************/
1269 : /* ResetReading() */
1270 : /************************************************************************/
1271 :
1272 3773 : void OGRCSVLayer::ResetReading()
1273 :
1274 : {
1275 3773 : if (fpCSV)
1276 3773 : VSIRewindL(fpCSV);
1277 :
1278 3773 : if (bHasFieldNames)
1279 3645 : CSLDestroy(CSVReadParseLine3L(fpCSV, m_nMaxLineSize, szDelimiter,
1280 3645 : bHonourStrings,
1281 : false, // bKeepLeadingAndClosingQuotes
1282 : false, // bMergeDelimiter,
1283 : true // bSkipBOM
1284 : ));
1285 :
1286 3773 : bNeedRewindBeforeRead = false;
1287 :
1288 3773 : m_nNextFID = FID_INITIAL_VALUE;
1289 3773 : }
1290 :
1291 : /************************************************************************/
1292 : /* GetNextLineTokens() */
1293 : /************************************************************************/
1294 :
1295 48825 : char **OGRCSVLayer::GetNextLineTokens()
1296 : {
1297 : while (true)
1298 : {
1299 : // Read the CSV record.
1300 97650 : char **papszTokens = CSVReadParseLine3L(
1301 48825 : fpCSV, m_nMaxLineSize, szDelimiter, bHonourStrings,
1302 : false, // bKeepLeadingAndClosingQuotes
1303 48825 : bMergeDelimiter,
1304 : true // bSkipBOM
1305 : );
1306 48825 : if (papszTokens == nullptr)
1307 1390 : return nullptr;
1308 :
1309 47435 : if (papszTokens[0] != nullptr)
1310 47435 : return papszTokens;
1311 :
1312 0 : CSLDestroy(papszTokens);
1313 0 : }
1314 : }
1315 :
1316 : /************************************************************************/
1317 : /* GetFeature() */
1318 : /************************************************************************/
1319 :
1320 59 : OGRFeature *OGRCSVLayer::GetFeature(GIntBig nFID)
1321 : {
1322 59 : if (nFID < FID_INITIAL_VALUE || fpCSV == nullptr)
1323 7 : return nullptr;
1324 52 : if (nFID < m_nNextFID || bNeedRewindBeforeRead)
1325 20 : ResetReading();
1326 109 : while (m_nNextFID < nFID)
1327 : {
1328 71 : char **papszTokens = GetNextLineTokens();
1329 71 : if (papszTokens == nullptr)
1330 14 : return nullptr;
1331 57 : CSLDestroy(papszTokens);
1332 57 : m_nNextFID++;
1333 : }
1334 38 : return GetNextUnfilteredFeature();
1335 : }
1336 :
1337 : /************************************************************************/
1338 : /* GetNextUnfilteredFeature() */
1339 : /************************************************************************/
1340 :
1341 47943 : OGRFeature *OGRCSVLayer::GetNextUnfilteredFeature()
1342 :
1343 : {
1344 47943 : if (fpCSV == nullptr)
1345 0 : return nullptr;
1346 :
1347 : // Read the CSV record.
1348 47943 : char **papszTokens = GetNextLineTokens();
1349 47943 : if (papszTokens == nullptr)
1350 1350 : return nullptr;
1351 :
1352 : // Create the OGR feature.
1353 46593 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
1354 :
1355 : // Set attributes for any indicated attribute records.
1356 46593 : int iOGRField = 0;
1357 : const int nAttrCount = std::min(
1358 46593 : CSLCount(papszTokens), nCSVFieldCount + (bHiddenWKTColumn ? 1 : 0));
1359 :
1360 257080 : for (int iAttr = 0; !bIsEurostatTSV && iAttr < nAttrCount; iAttr++)
1361 : {
1362 :
1363 : // Skip deleted fields if OGR_SCHEMA with schemaType=Full was specified and fields were removed
1364 210487 : if (OGRCSVDataSource *poCsvDs = static_cast<OGRCSVDataSource *>(m_poDS))
1365 : {
1366 210487 : if (!poCsvDs->DeletedFieldIndexes().empty())
1367 : {
1368 : const auto &deletedFieldIndexes =
1369 14 : poCsvDs->DeletedFieldIndexes();
1370 24 : if (std::find(deletedFieldIndexes.cbegin(),
1371 : deletedFieldIndexes.cend(),
1372 14 : iAttr) != deletedFieldIndexes.cend())
1373 : {
1374 40 : continue;
1375 : }
1376 : }
1377 : }
1378 :
1379 210477 : if ((iAttr == iLongitudeField || iAttr == iLatitudeField ||
1380 209599 : iAttr == iZField) &&
1381 1282 : !bKeepGeomColumns)
1382 : {
1383 14 : continue;
1384 : }
1385 210463 : int iGeom = 0;
1386 210463 : if (bHiddenWKTColumn)
1387 : {
1388 18 : if (iAttr != 0)
1389 9 : iGeom = panGeomFieldIndex[iAttr - 1];
1390 : }
1391 : else
1392 : {
1393 210445 : iGeom = panGeomFieldIndex[iAttr];
1394 : }
1395 210463 : if (iGeom >= 0)
1396 : {
1397 : const OGRGeomFieldDefn *poGeomFieldDefn =
1398 3499 : poFeatureDefn->GetGeomFieldDefn(iGeom);
1399 6230 : if (papszTokens[iAttr][0] != '\0' &&
1400 2731 : !(poGeomFieldDefn->IsIgnored()))
1401 : {
1402 2160 : const char *pszStr = papszTokens[iAttr];
1403 2160 : while (*pszStr == ' ')
1404 0 : pszStr++;
1405 4320 : std::unique_ptr<OGRGeometry> poGeom = nullptr;
1406 : OGRErr eErr;
1407 :
1408 2160 : if (EQUAL(poGeomFieldDefn->GetNameRef(), ""))
1409 : {
1410 321 : std::tie(poGeom, eErr) =
1411 642 : OGRGeometryFactory::createFromWkt(pszStr);
1412 321 : if (eErr != OGRERR_NONE)
1413 : {
1414 1 : CPLError(CE_Warning, CPLE_AppDefined,
1415 : "Ignoring invalid WKT: %s", pszStr);
1416 : }
1417 : }
1418 : else
1419 : {
1420 3678 : CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
1421 :
1422 1839 : std::tie(poGeom, eErr) =
1423 3678 : OGRGeometryFactory::createFromWkt(pszStr);
1424 :
1425 1839 : if (!poGeom && *pszStr == '{')
1426 : {
1427 1 : poGeom.reset(OGRGeometry::FromHandle(
1428 : OGR_G_CreateGeometryFromJson(pszStr)));
1429 : }
1430 1839 : else if (!poGeom && ((*pszStr >= '0' && *pszStr <= '9') ||
1431 1 : (*pszStr >= 'a' && *pszStr <= 'z') ||
1432 0 : (*pszStr >= 'A' && *pszStr <= 'Z')))
1433 : {
1434 5 : poGeom.reset(
1435 : OGRGeometryFromHexEWKB(pszStr, nullptr, FALSE));
1436 : }
1437 : }
1438 :
1439 2160 : if (poGeom)
1440 : {
1441 4316 : poGeom->assignSpatialReference(
1442 2158 : poGeomFieldDefn->GetSpatialRef());
1443 2158 : poFeature->SetGeomField(iGeom, std::move(poGeom));
1444 : }
1445 : }
1446 :
1447 3499 : const bool bHasAttributeField =
1448 3499 : bKeepGeomColumns && !(iAttr == 0 && bHiddenWKTColumn);
1449 3499 : if (!bHasAttributeField)
1450 16 : continue;
1451 : }
1452 :
1453 : const OGRFieldDefn *poFieldDefn =
1454 210447 : poFeatureDefn->GetFieldDefn(iOGRField);
1455 210447 : const OGRFieldType eFieldType = poFieldDefn->GetType();
1456 210447 : const OGRFieldSubType eFieldSubType = poFieldDefn->GetSubType();
1457 :
1458 38 : const auto WarnOnceBadValue = [this, poFieldDefn]()
1459 : {
1460 22 : if (!bWarningBadTypeOrWidth)
1461 : {
1462 8 : bWarningBadTypeOrWidth = true;
1463 8 : CPLError(CE_Warning, CPLE_AppDefined,
1464 : "Invalid value type found in record %" PRId64
1465 : " for field %s. "
1466 : "This warning will no longer be emitted",
1467 : m_nNextFID, poFieldDefn->GetNameRef());
1468 : };
1469 210469 : };
1470 :
1471 36 : const auto WarnTooLargeWidth = [this, poFieldDefn]()
1472 : {
1473 12 : if (!bWarningBadTypeOrWidth)
1474 : {
1475 12 : bWarningBadTypeOrWidth = true;
1476 12 : CPLError(CE_Warning, CPLE_AppDefined,
1477 : "Value with a width greater than field width "
1478 : "found in record %" PRId64 " for field %s. "
1479 : "This warning will no longer be emitted",
1480 : m_nNextFID, poFieldDefn->GetNameRef());
1481 : };
1482 210459 : };
1483 :
1484 210447 : if (eFieldType == OFTInteger && eFieldSubType == OFSTBoolean)
1485 : {
1486 14 : if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored())
1487 : {
1488 21 : if (OGRCSVIsTrue(papszTokens[iAttr]) ||
1489 7 : strcmp(papszTokens[iAttr], "1") == 0)
1490 : {
1491 8 : poFeature->SetField(iOGRField, 1);
1492 : }
1493 7 : else if (OGRCSVIsFalse(papszTokens[iAttr]) ||
1494 1 : strcmp(papszTokens[iAttr], "0") == 0)
1495 : {
1496 5 : poFeature->SetField(iOGRField, 0);
1497 : }
1498 : else
1499 : {
1500 : // Set to TRUE because it's different than 0 but emit a warning
1501 1 : poFeature->SetField(iOGRField, 1);
1502 1 : WarnOnceBadValue();
1503 : }
1504 : }
1505 : }
1506 210433 : else if (eFieldType == OFTInteger || eFieldType == OFTInteger64)
1507 : {
1508 224 : if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored())
1509 : {
1510 157 : char *endptr = nullptr;
1511 : const GIntBig nVal = static_cast<GIntBig>(
1512 157 : std::strtoll(papszTokens[iAttr], &endptr, 10));
1513 157 : if (endptr == papszTokens[iAttr] + strlen(papszTokens[iAttr]))
1514 : {
1515 144 : poFeature->SetField(iOGRField, nVal);
1516 139 : if (!bWarningBadTypeOrWidth &&
1517 283 : poFieldDefn->GetWidth() > 0 &&
1518 17 : static_cast<int>(strlen(papszTokens[iAttr])) >
1519 17 : poFieldDefn->GetWidth())
1520 : {
1521 1 : WarnTooLargeWidth();
1522 : }
1523 : }
1524 : else
1525 : {
1526 13 : WarnOnceBadValue();
1527 : }
1528 224 : }
1529 : }
1530 210209 : else if (eFieldType == OFTReal)
1531 : {
1532 3367 : if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored())
1533 : {
1534 2900 : char *chComma = strchr(papszTokens[iAttr], ',');
1535 2900 : if (chComma)
1536 5 : *chComma = '.';
1537 2900 : char *endptr = nullptr;
1538 : const double dfVal =
1539 2900 : CPLStrtodDelim(papszTokens[iAttr], &endptr, '.');
1540 2900 : if (endptr == papszTokens[iAttr] + strlen(papszTokens[iAttr]))
1541 : {
1542 2893 : poFeature->SetField(iOGRField, dfVal);
1543 2889 : if (!bWarningBadTypeOrWidth &&
1544 5782 : poFieldDefn->GetWidth() > 0 &&
1545 53 : static_cast<int>(strlen(papszTokens[iAttr])) >
1546 53 : poFieldDefn->GetWidth())
1547 : {
1548 10 : WarnTooLargeWidth();
1549 : }
1550 5762 : else if (!bWarningBadTypeOrWidth &&
1551 2879 : poFieldDefn->GetWidth() > 0)
1552 : {
1553 43 : const char *pszDot = strchr(papszTokens[iAttr], '.');
1554 43 : const int nPrecision =
1555 : pszDot != nullptr
1556 43 : ? static_cast<int>(strlen(pszDot + 1))
1557 : : 0;
1558 43 : if (nPrecision > poFieldDefn->GetPrecision())
1559 : {
1560 1 : bWarningBadTypeOrWidth = true;
1561 1 : CPLError(CE_Warning, CPLE_AppDefined,
1562 : "Value with a precision greater than "
1563 : "field precision found in record %" PRId64
1564 : " for field %s. "
1565 : "This warning will no longer be emitted",
1566 : m_nNextFID, poFieldDefn->GetNameRef());
1567 : }
1568 : }
1569 : }
1570 : else
1571 : {
1572 7 : WarnOnceBadValue();
1573 : }
1574 : }
1575 : }
1576 206842 : else if (eFieldType != OFTString)
1577 : {
1578 218 : if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored())
1579 : {
1580 109 : poFeature->SetField(iOGRField, papszTokens[iAttr]);
1581 212 : if (!bWarningBadTypeOrWidth &&
1582 103 : !poFeature->IsFieldSetAndNotNull(iOGRField))
1583 : {
1584 1 : WarnOnceBadValue();
1585 : }
1586 : }
1587 : }
1588 206624 : else if (!poFieldDefn->IsIgnored())
1589 : {
1590 204966 : if (bEmptyStringNull && papszTokens[iAttr][0] == '\0')
1591 : {
1592 1 : poFeature->SetFieldNull(iOGRField);
1593 : }
1594 : else
1595 : {
1596 204965 : poFeature->SetField(iOGRField, papszTokens[iAttr]);
1597 205010 : if (!bWarningBadTypeOrWidth && poFieldDefn->GetWidth() > 0 &&
1598 45 : static_cast<int>(strlen(papszTokens[iAttr])) >
1599 45 : poFieldDefn->GetWidth())
1600 : {
1601 1 : WarnTooLargeWidth();
1602 : }
1603 : }
1604 : }
1605 :
1606 210447 : if (bKeepSourceColumns && eFieldType != OFTString)
1607 : {
1608 11 : iOGRField++;
1609 21 : if (papszTokens[iAttr][0] != '\0' &&
1610 10 : !poFeatureDefn->GetFieldDefn(iOGRField)->IsIgnored())
1611 : {
1612 10 : poFeature->SetField(iOGRField, papszTokens[iAttr]);
1613 : }
1614 : }
1615 :
1616 210447 : iOGRField++;
1617 : }
1618 :
1619 : // Eurostat TSV files.
1620 :
1621 46597 : for (int iAttr = 0; bIsEurostatTSV && iAttr < nAttrCount; iAttr++)
1622 : {
1623 4 : if (iAttr == 0)
1624 : {
1625 1 : char **papszDims = CSLTokenizeString2(papszTokens[0], ",", 0);
1626 1 : if (CSLCount(papszDims) != nEurostatDims)
1627 : {
1628 0 : CSLDestroy(papszDims);
1629 0 : break;
1630 : }
1631 3 : for (int iSubAttr = 0; iSubAttr < nEurostatDims; iSubAttr++)
1632 : {
1633 2 : if (!poFeatureDefn->GetFieldDefn(iSubAttr)->IsIgnored())
1634 2 : poFeature->SetField(iSubAttr, papszDims[iSubAttr]);
1635 : }
1636 1 : CSLDestroy(papszDims);
1637 : }
1638 : else
1639 : {
1640 3 : char **papszVals = CSLTokenizeString2(papszTokens[iAttr], " ", 0);
1641 3 : CPLValueType eType = CPLGetValueType(papszVals[0]);
1642 3 : if ((papszVals[0] && papszVals[0][0] != '\0') &&
1643 2 : (eType == CPL_VALUE_INTEGER || eType == CPL_VALUE_REAL))
1644 : {
1645 2 : if (!poFeatureDefn
1646 2 : ->GetFieldDefn(nEurostatDims + 2 * (iAttr - 1))
1647 2 : ->IsIgnored())
1648 2 : poFeature->SetField(nEurostatDims + 2 * (iAttr - 1),
1649 : papszVals[0]);
1650 : }
1651 3 : if (CSLCount(papszVals) == 2)
1652 : {
1653 1 : if (!poFeatureDefn
1654 1 : ->GetFieldDefn(nEurostatDims + 2 * (iAttr - 1) + 1)
1655 1 : ->IsIgnored())
1656 1 : poFeature->SetField(nEurostatDims + 2 * (iAttr - 1) + 1,
1657 1 : papszVals[1]);
1658 : }
1659 3 : CSLDestroy(papszVals);
1660 : }
1661 : }
1662 :
1663 : // Is it a numeric value parsable by local-aware CPLAtofM()
1664 1271 : const auto IsCPLAtofMParsable = [](char *pszVal)
1665 : {
1666 1271 : auto l_eType = CPLGetValueType(pszVal);
1667 1271 : if (l_eType == CPL_VALUE_INTEGER || l_eType == CPL_VALUE_REAL)
1668 1271 : return true;
1669 0 : char *pszComma = strchr(pszVal, ',');
1670 0 : if (pszComma)
1671 : {
1672 0 : *pszComma = '.';
1673 0 : l_eType = CPLGetValueType(pszVal);
1674 0 : *pszComma = ',';
1675 : }
1676 0 : return l_eType == CPL_VALUE_REAL;
1677 : };
1678 :
1679 : // http://www.faa.gov/airports/airport_safety/airportdata_5010/menu/index.cfm
1680 : // specific
1681 :
1682 46593 : if (iNfdcLatitudeS != -1 && iNfdcLongitudeS != -1 &&
1683 0 : nAttrCount > iNfdcLatitudeS && nAttrCount > iNfdcLongitudeS &&
1684 0 : papszTokens[iNfdcLongitudeS][0] != 0 &&
1685 0 : papszTokens[iNfdcLatitudeS][0] != 0)
1686 : {
1687 : const double dfLon =
1688 0 : CPLAtof(papszTokens[iNfdcLongitudeS]) / 3600.0 *
1689 0 : (strchr(papszTokens[iNfdcLongitudeS], 'W') ? -1.0 : 1.0);
1690 : const double dfLat =
1691 0 : CPLAtof(papszTokens[iNfdcLatitudeS]) / 3600.0 *
1692 0 : (strchr(papszTokens[iNfdcLatitudeS], 'S') ? -1.0 : 1.0);
1693 0 : if (!poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
1694 0 : poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
1695 : }
1696 :
1697 439 : else if (iLatitudeField != -1 && iLongitudeField != -1 &&
1698 439 : nAttrCount > iLatitudeField && nAttrCount > iLongitudeField &&
1699 439 : papszTokens[iLongitudeField][0] != 0 &&
1700 434 : papszTokens[iLatitudeField][0] != 0 &&
1701 47466 : IsCPLAtofMParsable(papszTokens[iLongitudeField]) &&
1702 434 : IsCPLAtofMParsable(papszTokens[iLatitudeField]))
1703 : {
1704 434 : if (!m_bIsGNIS ||
1705 : // GNIS specific: some records have dummy 0,0 value.
1706 0 : (papszTokens[iLongitudeField][0] != DIGIT_ZERO ||
1707 0 : papszTokens[iLongitudeField][1] != '\0' ||
1708 0 : papszTokens[iLatitudeField][0] != DIGIT_ZERO ||
1709 0 : papszTokens[iLatitudeField][1] != '\0'))
1710 : {
1711 434 : const double dfLon = CPLAtofM(papszTokens[iLongitudeField]);
1712 434 : const double dfLat = CPLAtofM(papszTokens[iLatitudeField]);
1713 434 : if (!poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
1714 : {
1715 404 : if (iZField != -1 && nAttrCount > iZField &&
1716 1241 : papszTokens[iZField][0] != 0 &&
1717 403 : IsCPLAtofMParsable(papszTokens[iZField]))
1718 403 : poFeature->SetGeometryDirectly(new OGRPoint(
1719 403 : dfLon, dfLat, CPLAtofM(papszTokens[iZField])));
1720 : else
1721 31 : poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
1722 : }
1723 : }
1724 : }
1725 :
1726 46593 : CSLDestroy(papszTokens);
1727 :
1728 46593 : if ((m_nNextFID % 100000) == 0)
1729 : {
1730 0 : CPLDebug("CSV", "FID = %" PRId64 ", file offset = %" PRIu64, m_nNextFID,
1731 0 : static_cast<uint64_t>(fpCSV->Tell()));
1732 : }
1733 :
1734 : // Translate the record id.
1735 46593 : poFeature->SetFID(m_nNextFID++);
1736 :
1737 46593 : m_nFeaturesRead++;
1738 :
1739 46593 : return poFeature;
1740 : }
1741 :
1742 : /************************************************************************/
1743 : /* GetNextFeature() */
1744 : /************************************************************************/
1745 :
1746 47224 : OGRFeature *OGRCSVLayer::GetNextFeature()
1747 :
1748 : {
1749 47224 : if (bNeedRewindBeforeRead)
1750 3 : ResetReading();
1751 :
1752 : // Read features till we find one that satisfies our current
1753 : // spatial criteria.
1754 : while (true)
1755 : {
1756 47905 : OGRFeature *poFeature = GetNextUnfilteredFeature();
1757 47905 : if (poFeature == nullptr)
1758 1348 : return nullptr;
1759 :
1760 93458 : if ((m_poFilterGeom == nullptr ||
1761 93007 : FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
1762 46450 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
1763 45876 : return poFeature;
1764 :
1765 681 : delete poFeature;
1766 681 : }
1767 : }
1768 :
1769 : /************************************************************************/
1770 : /* TestCapability() */
1771 : /************************************************************************/
1772 :
1773 4226 : int OGRCSVLayer::TestCapability(const char *pszCap)
1774 :
1775 : {
1776 4226 : if (EQUAL(pszCap, OLCSequentialWrite))
1777 294 : return bInWriteMode && !bKeepSourceColumns && bKeepGeomColumns;
1778 3932 : else if (EQUAL(pszCap, OLCCreateField))
1779 885 : return bNew && !bHasFieldNames;
1780 3047 : else if (EQUAL(pszCap, OLCCreateGeomField))
1781 72 : return bNew && !bHasFieldNames &&
1782 72 : eGeometryFormat == OGR_CSV_GEOM_AS_WKT;
1783 3011 : else if (EQUAL(pszCap, OLCIgnoreFields))
1784 1815 : return TRUE;
1785 1196 : else if (EQUAL(pszCap, OLCCurveGeometries))
1786 147 : return TRUE;
1787 1049 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
1788 279 : return TRUE;
1789 770 : else if (EQUAL(pszCap, OLCZGeometries))
1790 18 : return TRUE;
1791 : else
1792 752 : return FALSE;
1793 : }
1794 :
1795 : /************************************************************************/
1796 : /* PreCreateField() */
1797 : /************************************************************************/
1798 :
1799 : OGRCSVCreateFieldAction
1800 832 : OGRCSVLayer::PreCreateField(OGRFeatureDefn *poFeatureDefn,
1801 : const std::set<CPLString> &oSetFields,
1802 : const OGRFieldDefn *poNewField, int bApproxOK)
1803 : {
1804 : // Does this duplicate an existing field?
1805 832 : if (oSetFields.find(CPLString(poNewField->GetNameRef()).toupper()) !=
1806 1664 : oSetFields.end())
1807 : {
1808 6 : if (poFeatureDefn->GetGeomFieldIndex(poNewField->GetNameRef()) >= 0 ||
1809 3 : poFeatureDefn->GetGeomFieldIndex(
1810 3 : CPLSPrintf("geom_%s", poNewField->GetNameRef())) >= 0)
1811 : {
1812 2 : return CREATE_FIELD_DO_NOTHING;
1813 : }
1814 1 : CPLError(CE_Failure, CPLE_AppDefined,
1815 : "Attempt to create field %s, "
1816 : "but a field with this name already exists.",
1817 : poNewField->GetNameRef());
1818 :
1819 1 : return CREATE_FIELD_ERROR;
1820 : }
1821 :
1822 : // Is this a legal field type for CSV?
1823 829 : switch (poNewField->GetType())
1824 : {
1825 829 : case OFTInteger:
1826 : case OFTInteger64:
1827 : case OFTReal:
1828 : case OFTString:
1829 : case OFTIntegerList:
1830 : case OFTInteger64List:
1831 : case OFTRealList:
1832 : case OFTStringList:
1833 : case OFTTime:
1834 : case OFTDate:
1835 : case OFTDateTime:
1836 : // These types are OK.
1837 829 : break;
1838 :
1839 0 : default:
1840 0 : if (bApproxOK)
1841 : {
1842 0 : CPLError(CE_Warning, CPLE_AppDefined,
1843 : "Attempt to create field of type %s, but this is not "
1844 : "supported "
1845 : "for .csv files. Just treating as a plain string.",
1846 : poNewField->GetFieldTypeName(poNewField->GetType()));
1847 : }
1848 : else
1849 : {
1850 0 : CPLError(CE_Failure, CPLE_AppDefined,
1851 : "Attempt to create field of type %s, but this is not "
1852 : "supported "
1853 : "for .csv files.",
1854 : poNewField->GetFieldTypeName(poNewField->GetType()));
1855 0 : return CREATE_FIELD_ERROR;
1856 : }
1857 : }
1858 829 : return CREATE_FIELD_PROCEED;
1859 : }
1860 :
1861 : /************************************************************************/
1862 : /* CreateField() */
1863 : /************************************************************************/
1864 :
1865 427 : OGRErr OGRCSVLayer::CreateField(const OGRFieldDefn *poNewField, int bApproxOK)
1866 :
1867 : {
1868 : // If we have already written our field names, then we are not
1869 : // allowed to add new fields.
1870 427 : if (!TestCapability(OLCCreateField))
1871 : {
1872 1 : CPLError(CE_Failure, CPLE_AppDefined,
1873 : "Unable to create new fields after first feature written.");
1874 1 : return OGRERR_FAILURE;
1875 : }
1876 :
1877 426 : if (nCSVFieldCount >= 10000)
1878 : {
1879 0 : CPLError(CE_Failure, CPLE_AppDefined, "Limiting to 10000 fields");
1880 0 : return OGRERR_FAILURE;
1881 : }
1882 :
1883 426 : if (m_oSetFields.empty())
1884 : {
1885 120 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
1886 : {
1887 : m_oSetFields.insert(
1888 28 : CPLString(poFeatureDefn->GetFieldDefn(i)->GetNameRef())
1889 28 : .toupper());
1890 : }
1891 : }
1892 :
1893 : const OGRCSVCreateFieldAction eAction =
1894 426 : PreCreateField(poFeatureDefn, m_oSetFields, poNewField, bApproxOK);
1895 426 : if (eAction == CREATE_FIELD_DO_NOTHING)
1896 2 : return OGRERR_NONE;
1897 424 : if (eAction == CREATE_FIELD_ERROR)
1898 0 : return OGRERR_FAILURE;
1899 :
1900 : // Seems ok, add to field list.
1901 424 : poFeatureDefn->AddFieldDefn(poNewField);
1902 424 : nCSVFieldCount++;
1903 424 : m_oSetFields.insert(CPLString(poNewField->GetNameRef()).toupper());
1904 :
1905 848 : panGeomFieldIndex = static_cast<int *>(CPLRealloc(
1906 424 : panGeomFieldIndex, sizeof(int) * poFeatureDefn->GetFieldCount()));
1907 424 : panGeomFieldIndex[poFeatureDefn->GetFieldCount() - 1] = -1;
1908 :
1909 424 : return OGRERR_NONE;
1910 : }
1911 :
1912 : /************************************************************************/
1913 : /* CreateGeomField() */
1914 : /************************************************************************/
1915 :
1916 21 : OGRErr OGRCSVLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomField,
1917 : int /* bApproxOK */)
1918 : {
1919 21 : if (!TestCapability(OLCCreateGeomField))
1920 : {
1921 0 : CPLError(CE_Failure, CPLE_AppDefined,
1922 : "Unable to create new fields after first feature written.");
1923 0 : return OGRERR_FAILURE;
1924 : }
1925 :
1926 : // Does this duplicate an existing field?
1927 21 : if (poFeatureDefn->GetGeomFieldIndex(poGeomField->GetNameRef()) >= 0)
1928 : {
1929 1 : CPLError(CE_Failure, CPLE_AppDefined,
1930 : "Attempt to create geom field %s, "
1931 : "but a field with this name already exists.",
1932 : poGeomField->GetNameRef());
1933 :
1934 1 : return OGRERR_FAILURE;
1935 : }
1936 40 : OGRGeomFieldDefn oGeomField(poGeomField);
1937 20 : auto poSRSOri = poGeomField->GetSpatialRef();
1938 20 : if (poSRSOri)
1939 : {
1940 6 : auto poSRS = poSRSOri->Clone();
1941 6 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1942 6 : oGeomField.SetSpatialRef(poSRS);
1943 6 : poSRS->Release();
1944 : }
1945 20 : poFeatureDefn->AddGeomFieldDefn(&oGeomField);
1946 :
1947 20 : const char *pszName = poGeomField->GetNameRef();
1948 20 : if (EQUAL(pszName, ""))
1949 : {
1950 3 : const int nIdx = poFeatureDefn->GetFieldIndex("WKT");
1951 3 : if (nIdx >= 0)
1952 : {
1953 0 : panGeomFieldIndex[nIdx] = poFeatureDefn->GetGeomFieldCount() - 1;
1954 0 : return OGRERR_NONE;
1955 : }
1956 3 : pszName = "WKT";
1957 : }
1958 20 : if (STARTS_WITH_CI(pszName, "geom_") && strlen(pszName) >= strlen("geom_"))
1959 15 : pszName += strlen("geom_");
1960 20 : if (!EQUAL(pszName, "WKT") && !STARTS_WITH_CI(pszName, "_WKT"))
1961 2 : pszName = CPLSPrintf("_WKT%s", pszName);
1962 :
1963 20 : OGRFieldDefn oRegularFieldDefn(pszName, OFTString);
1964 20 : poFeatureDefn->AddFieldDefn(&oRegularFieldDefn);
1965 20 : nCSVFieldCount++;
1966 :
1967 40 : panGeomFieldIndex = static_cast<int *>(CPLRealloc(
1968 20 : panGeomFieldIndex, sizeof(int) * poFeatureDefn->GetFieldCount()));
1969 40 : panGeomFieldIndex[poFeatureDefn->GetFieldCount() - 1] =
1970 20 : poFeatureDefn->GetGeomFieldCount() - 1;
1971 :
1972 20 : return OGRERR_NONE;
1973 : }
1974 :
1975 : /************************************************************************/
1976 : /* WriteHeader() */
1977 : /* */
1978 : /* Write the header, and possibly the .csvt file if they */
1979 : /* haven't already been written. */
1980 : /************************************************************************/
1981 :
1982 133 : OGRErr OGRCSVLayer::WriteHeader()
1983 : {
1984 133 : if (!bNew)
1985 0 : return OGRERR_NONE;
1986 :
1987 : // Write field names if we haven't written them yet.
1988 : // Write .csvt file if needed.
1989 133 : bNew = false;
1990 133 : bHasFieldNames = true;
1991 133 : bool bOK = true;
1992 :
1993 298 : for (int iFile = 0; iFile < (bCreateCSVT ? 2 : 1); iFile++)
1994 : {
1995 165 : VSILFILE *fpCSVT = nullptr;
1996 165 : if (bCreateCSVT && iFile == 0)
1997 : {
1998 : char *pszDirName =
1999 32 : CPLStrdup(CPLGetDirnameSafe(pszFilename).c_str());
2000 : char *pszBaseName =
2001 32 : CPLStrdup(CPLGetBasenameSafe(pszFilename).c_str());
2002 32 : fpCSVT = VSIFOpenL(
2003 64 : CPLFormFilenameSafe(pszDirName, pszBaseName, ".csvt").c_str(),
2004 : "wb");
2005 32 : CPLFree(pszDirName);
2006 32 : CPLFree(pszBaseName);
2007 : }
2008 : else
2009 : {
2010 133 : if (STARTS_WITH(pszFilename, "/vsistdout/") ||
2011 132 : STARTS_WITH(pszFilename, "/vsizip/"))
2012 1 : fpCSV = VSIFOpenL(pszFilename, "wb");
2013 : else
2014 132 : fpCSV = VSIFOpenL(pszFilename, "w+b");
2015 :
2016 133 : if (fpCSV == nullptr)
2017 : {
2018 0 : CPLError(CE_Failure, CPLE_OpenFailed,
2019 : "Failed to create %s:\n%s", pszFilename,
2020 0 : VSIStrerror(errno));
2021 0 : return OGRERR_FAILURE;
2022 : }
2023 : }
2024 :
2025 165 : if (bWriteBOM && fpCSV)
2026 : {
2027 2 : bOK &= VSIFWriteL("\xEF\xBB\xBF", 1, 3, fpCSV) > 0;
2028 : }
2029 :
2030 165 : bool bNeedDelimiter = false;
2031 165 : if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ)
2032 : {
2033 3 : if (fpCSV)
2034 2 : bOK &=
2035 2 : VSIFPrintfL(fpCSV, "X%sY%sZ", szDelimiter, szDelimiter) > 0;
2036 3 : if (fpCSVT)
2037 1 : bOK &= VSIFPrintfL(fpCSVT, "%s", "CoordX,CoordY,Real") > 0;
2038 3 : bNeedDelimiter = true;
2039 : }
2040 162 : else if (eGeometryFormat == OGR_CSV_GEOM_AS_XY)
2041 : {
2042 4 : if (fpCSV)
2043 2 : bOK &= VSIFPrintfL(fpCSV, "X%sY", szDelimiter) > 0;
2044 4 : if (fpCSVT)
2045 2 : bOK &= VSIFPrintfL(fpCSVT, "%s", "CoordX,CoordY") > 0;
2046 4 : bNeedDelimiter = true;
2047 : }
2048 158 : else if (eGeometryFormat == OGR_CSV_GEOM_AS_YX)
2049 : {
2050 2 : if (fpCSV)
2051 1 : bOK &= VSIFPrintfL(fpCSV, "Y%sX", szDelimiter) > 0;
2052 2 : if (fpCSVT)
2053 1 : bOK &= VSIFPrintfL(fpCSVT, "%s", "CoordY,CoordX") > 0;
2054 2 : bNeedDelimiter = true;
2055 : }
2056 156 : else if (bHiddenWKTColumn)
2057 : {
2058 49 : if (fpCSV)
2059 : {
2060 : const char *pszColName =
2061 35 : poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
2062 35 : bOK &= VSIFPrintfL(fpCSV, "%s", pszColName) >= 0;
2063 : }
2064 49 : if (fpCSVT)
2065 14 : bOK &= VSIFPrintfL(fpCSVT, "%s", "WKT") > 0;
2066 49 : bNeedDelimiter = true;
2067 : }
2068 :
2069 786 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
2070 : {
2071 621 : if (bNeedDelimiter)
2072 : {
2073 532 : if (fpCSV)
2074 369 : bOK &= VSIFPrintfL(fpCSV, "%s", szDelimiter) > 0;
2075 532 : if (fpCSVT)
2076 163 : bOK &= VSIFPrintfL(fpCSVT, "%s", ",") > 0;
2077 : }
2078 621 : bNeedDelimiter = true;
2079 :
2080 1242 : char *pszEscaped = CPLEscapeString(
2081 621 : poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), -1,
2082 621 : m_eStringQuoting == StringQuoting::ALWAYS
2083 : ? CPLES_CSV_FORCE_QUOTING
2084 : : CPLES_CSV);
2085 621 : if (pszEscaped == nullptr)
2086 0 : return OGRERR_FAILURE;
2087 :
2088 621 : if (fpCSV)
2089 : {
2090 444 : bool bAddDoubleQuote = false;
2091 444 : if (szDelimiter[0] == ' ' && pszEscaped[0] != '"' &&
2092 2 : strchr(pszEscaped, ' ') != nullptr)
2093 1 : bAddDoubleQuote = true;
2094 444 : if (bAddDoubleQuote)
2095 1 : bOK &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
2096 444 : bOK &= VSIFPrintfL(fpCSV, "%s", pszEscaped) >= 0;
2097 444 : if (bAddDoubleQuote)
2098 1 : bOK &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
2099 : }
2100 621 : CPLFree(pszEscaped);
2101 :
2102 621 : if (fpCSVT)
2103 : {
2104 177 : int nWidth = poFeatureDefn->GetFieldDefn(iField)->GetWidth();
2105 : const int nPrecision =
2106 177 : poFeatureDefn->GetFieldDefn(iField)->GetPrecision();
2107 :
2108 177 : switch (poFeatureDefn->GetFieldDefn(iField)->GetType())
2109 : {
2110 54 : case OFTInteger:
2111 : {
2112 54 : if (poFeatureDefn->GetFieldDefn(iField)->GetSubType() ==
2113 : OFSTBoolean)
2114 : {
2115 9 : nWidth = 0;
2116 9 : bOK &= VSIFPrintfL(fpCSVT, "%s",
2117 9 : "Integer(Boolean)") > 0;
2118 : }
2119 90 : else if (poFeatureDefn->GetFieldDefn(iField)
2120 45 : ->GetSubType() == OFSTInt16)
2121 : {
2122 11 : nWidth = 0;
2123 11 : bOK &=
2124 11 : VSIFPrintfL(fpCSVT, "%s", "Integer(Int16)") > 0;
2125 : }
2126 : else
2127 : {
2128 34 : bOK &= VSIFPrintfL(fpCSVT, "%s", "Integer") > 0;
2129 : }
2130 54 : break;
2131 : }
2132 14 : case OFTInteger64:
2133 14 : bOK &= VSIFPrintfL(fpCSVT, "%s", "Integer64") > 0;
2134 14 : break;
2135 46 : case OFTReal:
2136 : {
2137 46 : if (poFeatureDefn->GetFieldDefn(iField)->GetSubType() ==
2138 : OFSTFloat32)
2139 : {
2140 11 : nWidth = 0;
2141 11 : bOK &=
2142 11 : VSIFPrintfL(fpCSVT, "%s", "Real(Float32)") > 0;
2143 : }
2144 : else
2145 : {
2146 35 : bOK &= VSIFPrintfL(fpCSVT, "%s", "Real") > 0;
2147 : }
2148 46 : break;
2149 : }
2150 6 : case OFTDate:
2151 6 : bOK &= VSIFPrintfL(fpCSVT, "%s", "Date") > 0;
2152 6 : break;
2153 1 : case OFTTime:
2154 1 : bOK &= VSIFPrintfL(fpCSVT, "%s", "Time") > 0;
2155 1 : break;
2156 11 : case OFTDateTime:
2157 11 : bOK &= VSIFPrintfL(fpCSVT, "%s", "DateTime") > 0;
2158 11 : break;
2159 3 : case OFTStringList:
2160 3 : bOK &= VSIFPrintfL(fpCSVT, "%s", "JSonStringList") > 0;
2161 3 : break;
2162 2 : case OFTIntegerList:
2163 2 : bOK &= VSIFPrintfL(fpCSVT, "%s", "JSonIntegerList") > 0;
2164 2 : break;
2165 2 : case OFTInteger64List:
2166 2 : bOK &=
2167 2 : VSIFPrintfL(fpCSVT, "%s", "JSonInteger64List") > 0;
2168 2 : break;
2169 2 : case OFTRealList:
2170 2 : bOK &= VSIFPrintfL(fpCSVT, "%s", "JSonRealList") > 0;
2171 2 : break;
2172 36 : default:
2173 36 : bOK &= VSIFPrintfL(fpCSVT, "%s", "String") > 0;
2174 36 : break;
2175 : }
2176 :
2177 177 : if (nWidth != 0)
2178 : {
2179 22 : if (nPrecision != 0)
2180 9 : bOK &= VSIFPrintfL(fpCSVT, "(%d.%d)", nWidth,
2181 9 : nPrecision) > 0;
2182 : else
2183 13 : bOK &= VSIFPrintfL(fpCSVT, "(%d)", nWidth) > 0;
2184 : }
2185 : }
2186 : }
2187 :
2188 165 : if (bUseCRLF)
2189 : {
2190 1 : if (fpCSV)
2191 1 : bOK &= VSIFPutcL(13, fpCSV) > 0;
2192 1 : if (fpCSVT)
2193 0 : bOK &= VSIFPutcL(13, fpCSVT) > 0;
2194 : }
2195 165 : if (fpCSV)
2196 133 : bOK &= VSIFPutcL('\n', fpCSV) > 0;
2197 165 : if (fpCSVT)
2198 32 : bOK &= VSIFPutcL('\n', fpCSVT) > 0;
2199 165 : if (fpCSVT)
2200 32 : VSIFCloseL(fpCSVT);
2201 : }
2202 :
2203 133 : return (!bOK || fpCSV == nullptr) ? OGRERR_FAILURE : OGRERR_NONE;
2204 : }
2205 :
2206 : /************************************************************************/
2207 : /* ICreateFeature() */
2208 : /************************************************************************/
2209 :
2210 285 : OGRErr OGRCSVLayer::ICreateFeature(OGRFeature *poNewFeature)
2211 :
2212 : {
2213 285 : if (!bInWriteMode)
2214 : {
2215 1 : CPLError(CE_Failure, CPLE_AppDefined,
2216 : "The CreateFeature() operation is not permitted on a "
2217 : "read-only CSV.");
2218 1 : return OGRERR_FAILURE;
2219 : }
2220 :
2221 : // If we need rewind, it means that we have just written a feature before
2222 : // so there's no point seeking to the end of the file, as we're already
2223 : // at the end.
2224 284 : bool bNeedSeekEnd = !bNeedRewindBeforeRead;
2225 :
2226 284 : bNeedRewindBeforeRead = true;
2227 :
2228 : // Write field names if we haven't written them yet.
2229 : // Write .csvt file if needed.
2230 284 : if (bNew)
2231 : {
2232 106 : const OGRErr eErr = WriteHeader();
2233 106 : if (eErr != OGRERR_NONE)
2234 0 : return eErr;
2235 106 : bNeedSeekEnd = false;
2236 : }
2237 :
2238 284 : if (fpCSV == nullptr)
2239 0 : return OGRERR_FAILURE;
2240 :
2241 284 : bool bRet = true;
2242 :
2243 : // Make sure we are at the end of the file.
2244 284 : if (bNeedSeekEnd)
2245 : {
2246 21 : if (bFirstFeatureAppendedDuringSession)
2247 : {
2248 : // Add a newline character to the end of the file if necessary.
2249 21 : bFirstFeatureAppendedDuringSession = false;
2250 21 : bRet &= VSIFSeekL(fpCSV, 0, SEEK_END) >= 0;
2251 21 : bRet &= VSIFSeekL(fpCSV, VSIFTellL(fpCSV) - 1, SEEK_SET) >= 0;
2252 21 : char chLast = '\0';
2253 21 : bRet &= VSIFReadL(&chLast, 1, 1, fpCSV) > 0;
2254 21 : bRet &= VSIFSeekL(fpCSV, 0, SEEK_END) >= 0;
2255 21 : if (chLast != '\n')
2256 : {
2257 0 : if (bUseCRLF)
2258 0 : bRet &= VSIFPutcL(13, fpCSV) != EOF;
2259 0 : bRet &= VSIFPutcL('\n', fpCSV) != EOF;
2260 : }
2261 : }
2262 : else
2263 : {
2264 0 : bRet &= VSIFSeekL(fpCSV, 0, SEEK_END) >= 0;
2265 : }
2266 : }
2267 :
2268 284 : bool bNeedDelimiter = false;
2269 284 : bool bEmptyLine = true;
2270 :
2271 92 : const auto GetWktOptions = [](const OGRGeomFieldDefn *poGeomFieldDefn)
2272 : {
2273 92 : const auto &sCoordPrec = poGeomFieldDefn->GetCoordinatePrecision();
2274 :
2275 92 : OGRWktOptions wktOptions;
2276 92 : wktOptions.variant = wkbVariantIso;
2277 92 : if (sCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
2278 : {
2279 3 : wktOptions.format = OGRWktFormat::F;
2280 3 : wktOptions.xyPrecision =
2281 3 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
2282 3 : sCoordPrec.dfXYResolution);
2283 : }
2284 92 : if (sCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
2285 : {
2286 2 : wktOptions.format = OGRWktFormat::F;
2287 2 : wktOptions.zPrecision =
2288 2 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
2289 2 : sCoordPrec.dfZResolution);
2290 : }
2291 92 : if (sCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN)
2292 : {
2293 2 : wktOptions.format = OGRWktFormat::F;
2294 2 : wktOptions.mPrecision =
2295 2 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
2296 2 : sCoordPrec.dfMResolution);
2297 : }
2298 :
2299 92 : return wktOptions;
2300 : };
2301 :
2302 : // Write out the geometry.
2303 284 : if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ ||
2304 282 : eGeometryFormat == OGR_CSV_GEOM_AS_XY ||
2305 279 : eGeometryFormat == OGR_CSV_GEOM_AS_YX)
2306 : {
2307 6 : const OGRGeometry *poGeom = poNewFeature->GetGeometryRef();
2308 6 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
2309 : {
2310 5 : const auto poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
2311 5 : const OGRPoint *poPoint = poGeom->toPoint();
2312 5 : std::string osCoord;
2313 5 : if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ)
2314 4 : osCoord = OGRMakeWktCoordinate(poPoint->getX(), poPoint->getY(),
2315 : poPoint->getZ(), 3,
2316 6 : GetWktOptions(poGeomFieldDefn));
2317 3 : else if (eGeometryFormat == OGR_CSV_GEOM_AS_XY)
2318 : osCoord =
2319 4 : OGRMakeWktCoordinate(poPoint->getX(), poPoint->getY(), 0, 2,
2320 6 : GetWktOptions(poGeomFieldDefn));
2321 : else
2322 : osCoord =
2323 2 : OGRMakeWktCoordinate(poPoint->getY(), poPoint->getX(), 0, 2,
2324 3 : GetWktOptions(poGeomFieldDefn));
2325 :
2326 41 : for (char &ch : osCoord)
2327 : {
2328 36 : if (ch == ' ')
2329 7 : ch = szDelimiter[0];
2330 : }
2331 5 : bRet &= VSIFPrintfL(fpCSV, "%s", osCoord.c_str()) > 0;
2332 : }
2333 : else
2334 : {
2335 1 : bRet &= VSIFPrintfL(fpCSV, "%s", szDelimiter) > 0;
2336 1 : if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ)
2337 0 : bRet &= VSIFPrintfL(fpCSV, "%s", szDelimiter) > 0;
2338 : }
2339 6 : bEmptyLine = false;
2340 6 : bNeedDelimiter = true;
2341 : }
2342 278 : else if (bHiddenWKTColumn)
2343 : {
2344 74 : const OGRGeometry *poGeom = poNewFeature->GetGeomFieldRef(0);
2345 74 : if (poGeom)
2346 : {
2347 67 : const auto poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
2348 : const std::string wkt =
2349 134 : poGeom->exportToWkt(GetWktOptions(poGeomFieldDefn));
2350 67 : if (!wkt.empty())
2351 : {
2352 67 : bRet &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
2353 67 : bRet &= VSIFWriteL(wkt.c_str(), wkt.size(), 1, fpCSV) > 0;
2354 67 : bRet &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
2355 67 : bEmptyLine = false;
2356 : }
2357 : }
2358 74 : bNeedDelimiter = true;
2359 : }
2360 :
2361 : // Write out all the field values.
2362 1751 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
2363 : {
2364 1467 : char *pszEscaped = nullptr;
2365 :
2366 1467 : if (bNeedDelimiter)
2367 : {
2368 1263 : bRet &= VSIFPrintfL(fpCSV, "%s", szDelimiter) > 0;
2369 1263 : bEmptyLine = false;
2370 : }
2371 1467 : bNeedDelimiter = true;
2372 :
2373 1467 : if (eGeometryFormat == OGR_CSV_GEOM_AS_WKT &&
2374 530 : panGeomFieldIndex[iField] >= 0)
2375 : {
2376 24 : const int iGeom = panGeomFieldIndex[iField];
2377 24 : const OGRGeometry *poGeom = poNewFeature->GetGeomFieldRef(iGeom);
2378 24 : if (poGeom)
2379 : {
2380 : const auto poGeomFieldDefn =
2381 20 : poFeatureDefn->GetGeomFieldDefn(iGeom);
2382 : const std::string wkt =
2383 40 : poGeom->exportToWkt(GetWktOptions(poGeomFieldDefn));
2384 20 : if (!wkt.empty())
2385 : {
2386 : char *pszNew =
2387 20 : static_cast<char *>(CPLMalloc(1 + wkt.size() + 1 + 1));
2388 20 : pszNew[0] = '"';
2389 20 : memcpy(pszNew + 1, wkt.c_str(), wkt.size());
2390 20 : pszNew[1 + wkt.size()] = '"';
2391 20 : pszNew[1 + wkt.size() + 1] = '\0';
2392 20 : CPLFree(pszEscaped);
2393 20 : pszEscaped = pszNew;
2394 : }
2395 : else
2396 : {
2397 0 : CPLFree(pszEscaped);
2398 0 : pszEscaped = CPLStrdup("");
2399 : }
2400 : }
2401 : else
2402 : {
2403 4 : CPLFree(pszEscaped);
2404 4 : pszEscaped = CPLStrdup("");
2405 24 : }
2406 : }
2407 : else
2408 : {
2409 : const OGRFieldType eType(
2410 1443 : poFeatureDefn->GetFieldDefn(iField)->GetType());
2411 1443 : if (eType == OFTReal || eType == OFTInteger ||
2412 : eType == OFTInteger64)
2413 : {
2414 589 : if (poFeatureDefn->GetFieldDefn(iField)->GetSubType() ==
2415 620 : OFSTFloat32 &&
2416 31 : poNewFeature->IsFieldSetAndNotNull(iField))
2417 : {
2418 11 : pszEscaped = CPLStrdup(CPLSPrintf(
2419 : "%.8g", poNewFeature->GetFieldAsDouble(iField)));
2420 : }
2421 : else
2422 : {
2423 : pszEscaped =
2424 578 : CPLStrdup(poNewFeature->GetFieldAsString(iField));
2425 : }
2426 : }
2427 854 : else if (eType == OFTStringList || eType == OFTIntegerList ||
2428 847 : eType == OFTInteger64List || eType == OFTRealList)
2429 : {
2430 9 : char *pszJSon = poNewFeature->GetFieldAsSerializedJSon(iField);
2431 9 : if (pszJSon)
2432 : {
2433 9 : pszEscaped = CPLEscapeString(pszJSon, -1,
2434 9 : m_eStringQuoting ==
2435 : StringQuoting::ALWAYS
2436 : ? CPLES_CSV_FORCE_QUOTING
2437 : : CPLES_CSV);
2438 : }
2439 : else
2440 : {
2441 0 : pszEscaped = CPLStrdup("");
2442 : }
2443 9 : CPLFree(pszJSon);
2444 : }
2445 : else
2446 : {
2447 845 : const char *pszContent = poNewFeature->GetFieldAsString(iField);
2448 845 : pszEscaped = CPLEscapeString(
2449 : pszContent, -1,
2450 845 : (m_eStringQuoting == StringQuoting::ALWAYS ||
2451 835 : (m_eStringQuoting == StringQuoting::IF_AMBIGUOUS &&
2452 692 : (CPLGetValueType(pszContent) != CPL_VALUE_STRING ||
2453 565 : (pszContent[0] == DIGIT_ZERO && pszContent[1] != '\0' &&
2454 2 : pszContent[1] != '.'))))
2455 : ? CPLES_CSV_FORCE_QUOTING
2456 : : CPLES_CSV);
2457 : }
2458 : }
2459 1467 : if (pszEscaped == nullptr)
2460 : {
2461 0 : return OGRERR_FAILURE;
2462 : }
2463 1467 : const size_t nLen = strlen(pszEscaped);
2464 1467 : bool bAddDoubleQuote = false;
2465 1467 : if (szDelimiter[0] == ' ' && pszEscaped[0] != '"' &&
2466 2 : strchr(pszEscaped, ' ') != nullptr)
2467 1 : bAddDoubleQuote = true;
2468 1467 : if (bAddDoubleQuote)
2469 1 : bRet &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
2470 1467 : if (nLen)
2471 : {
2472 915 : bRet &= VSIFWriteL(pszEscaped, nLen, 1, fpCSV) > 0;
2473 915 : bEmptyLine = false;
2474 : }
2475 1467 : if (bAddDoubleQuote)
2476 1 : bRet &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
2477 1467 : CPLFree(pszEscaped);
2478 : }
2479 :
2480 284 : if (bEmptyLine)
2481 1 : bRet &= VSIFPrintfL(fpCSV, "\"\"") > 0;
2482 :
2483 284 : if (bUseCRLF)
2484 4 : bRet &= VSIFPutcL(13, fpCSV) != EOF;
2485 284 : bRet &= VSIFPutcL('\n', fpCSV) != EOF;
2486 :
2487 284 : if (nTotalFeatures >= 0)
2488 264 : nTotalFeatures++;
2489 :
2490 284 : return bRet ? OGRERR_NONE : OGRERR_FAILURE;
2491 : }
2492 :
2493 : /************************************************************************/
2494 : /* SetCRLF() */
2495 : /************************************************************************/
2496 :
2497 133 : void OGRCSVLayer::SetCRLF(bool bNewValue)
2498 : {
2499 133 : bUseCRLF = bNewValue;
2500 133 : }
2501 :
2502 : /************************************************************************/
2503 : /* SetWriteGeometry() */
2504 : /************************************************************************/
2505 :
2506 50 : void OGRCSVLayer::SetWriteGeometry(OGRwkbGeometryType eGType,
2507 : OGRCSVGeometryFormat eGeometryFormatIn,
2508 : const char *pszGeomCol)
2509 : {
2510 50 : eGeometryFormat = eGeometryFormatIn;
2511 50 : if (eGeometryFormat == OGR_CSV_GEOM_AS_WKT && eGType != wkbNone)
2512 : {
2513 70 : OGRGeomFieldDefn oGFld(pszGeomCol, eGType);
2514 35 : bHiddenWKTColumn = true;
2515 : // We don't use CreateGeomField() since we don't want to generate
2516 : // a geometry field in first position, as it confuses applications
2517 : // (such as MapServer <= 6.4) that assume that the first regular field
2518 : // they add will be at index 0.
2519 70 : poFeatureDefn->AddGeomFieldDefn(&oGFld);
2520 : }
2521 : else
2522 : {
2523 15 : poFeatureDefn->SetGeomType(eGType);
2524 : }
2525 50 : }
2526 :
2527 : /************************************************************************/
2528 : /* SetCreateCSVT() */
2529 : /************************************************************************/
2530 :
2531 37 : void OGRCSVLayer::SetCreateCSVT(bool bCreateCSVTIn)
2532 : {
2533 37 : bCreateCSVT = bCreateCSVTIn;
2534 37 : }
2535 :
2536 : /************************************************************************/
2537 : /* SetWriteBOM() */
2538 : /************************************************************************/
2539 :
2540 11 : void OGRCSVLayer::SetWriteBOM(bool bWriteBOMIn)
2541 : {
2542 11 : bWriteBOM = bWriteBOMIn;
2543 11 : }
2544 :
2545 : /************************************************************************/
2546 : /* GetFeatureCount() */
2547 : /************************************************************************/
2548 :
2549 106 : GIntBig OGRCSVLayer::GetFeatureCount(int bForce)
2550 : {
2551 106 : if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
2552 : {
2553 12 : return OGRLayer::GetFeatureCount(bForce);
2554 : }
2555 :
2556 94 : if (nTotalFeatures >= 0)
2557 67 : return nTotalFeatures;
2558 :
2559 27 : if (fpCSV == nullptr)
2560 0 : return 0;
2561 :
2562 27 : ResetReading();
2563 :
2564 27 : if (szDelimiter[0] == '\t' && !bHonourStrings)
2565 : {
2566 1 : const int nBufSize = 4096;
2567 1 : char szBuffer[nBufSize + 1] = {};
2568 :
2569 1 : nTotalFeatures = 0;
2570 1 : bool bLastWasNewLine = false;
2571 : while (true)
2572 : {
2573 : const int nRead =
2574 1 : static_cast<int>(VSIFReadL(szBuffer, 1, nBufSize, fpCSV));
2575 1 : szBuffer[nRead] = 0;
2576 1 : if (nTotalFeatures == 0 && szBuffer[0] != 13 && szBuffer[0] != 10)
2577 1 : nTotalFeatures = 1;
2578 1061 : for (int i = 0; i < nRead; i++)
2579 : {
2580 1060 : if (szBuffer[i] == 13 || szBuffer[i] == 10)
2581 : {
2582 10 : bLastWasNewLine = true;
2583 : }
2584 1050 : else if (bLastWasNewLine)
2585 : {
2586 9 : nTotalFeatures++;
2587 9 : bLastWasNewLine = false;
2588 : }
2589 : }
2590 :
2591 1 : if (nRead < nBufSize)
2592 1 : break;
2593 1 : }
2594 : }
2595 : else
2596 : {
2597 26 : nTotalFeatures = 0;
2598 : while (true)
2599 : {
2600 811 : char **papszTokens = GetNextLineTokens();
2601 811 : if (papszTokens == nullptr)
2602 26 : break;
2603 :
2604 785 : nTotalFeatures++;
2605 :
2606 785 : CSLDestroy(papszTokens);
2607 785 : }
2608 : }
2609 :
2610 27 : ResetReading();
2611 :
2612 27 : return nTotalFeatures;
2613 : }
2614 :
2615 : /************************************************************************/
2616 : /* SyncToDisk() */
2617 : /************************************************************************/
2618 :
2619 244 : OGRErr OGRCSVLayer::SyncToDisk()
2620 : {
2621 244 : if (bInWriteMode && fpCSV != nullptr)
2622 : {
2623 220 : if (VSIFFlushL(fpCSV) != 0)
2624 0 : return OGRERR_FAILURE;
2625 : }
2626 244 : return OGRERR_NONE;
2627 : }
|