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