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