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