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