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