Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CSV Translator
4 : * Purpose: Implements OGRCSVDataSource class
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2008-2013, 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 <cstring>
19 : #include <string>
20 :
21 : #include "cpl_conv.h"
22 : #include "cpl_csv.h"
23 : #include "cpl_error.h"
24 : #include "cpl_string.h"
25 : #include "cpl_vsi.h"
26 : #include "cpl_vsi_virtual.h"
27 : #include "ogr_core.h"
28 : #include "ogr_feature.h"
29 : #include "ogr_geometry.h"
30 : #include "ogr_spatialref.h"
31 : #include "ogreditablelayer.h"
32 : #include "ogrsf_frmts.h"
33 :
34 : /************************************************************************/
35 : /* OGRCSVEditableLayerSynchronizer */
36 : /************************************************************************/
37 :
38 : class OGRCSVEditableLayerSynchronizer final
39 : : public IOGREditableLayerSynchronizer
40 : {
41 : OGRCSVLayer *m_poCSVLayer;
42 : char **m_papszOpenOptions;
43 :
44 : public:
45 208 : OGRCSVEditableLayerSynchronizer(OGRCSVLayer *poCSVLayer,
46 : CSLConstList papszOpenOptions)
47 208 : : m_poCSVLayer(poCSVLayer),
48 208 : m_papszOpenOptions(CSLDuplicate(papszOpenOptions))
49 : {
50 208 : }
51 :
52 : virtual ~OGRCSVEditableLayerSynchronizer() override;
53 :
54 : virtual OGRErr EditableSyncToDisk(OGRLayer *poEditableLayer,
55 : OGRLayer **ppoDecoratedLayer) override;
56 :
57 18 : std::vector<std::string> GetFileList()
58 : {
59 18 : return m_poCSVLayer->GetFileList();
60 : }
61 : };
62 :
63 : /************************************************************************/
64 : /* ~OGRCSVEditableLayerSynchronizer() */
65 : /************************************************************************/
66 :
67 416 : OGRCSVEditableLayerSynchronizer::~OGRCSVEditableLayerSynchronizer()
68 : {
69 208 : CSLDestroy(m_papszOpenOptions);
70 416 : }
71 :
72 : /************************************************************************/
73 : /* EditableSyncToDisk() */
74 : /************************************************************************/
75 :
76 9 : OGRErr OGRCSVEditableLayerSynchronizer::EditableSyncToDisk(
77 : OGRLayer *poEditableLayer, OGRLayer **ppoDecoratedLayer)
78 : {
79 9 : CPLAssert(m_poCSVLayer == *ppoDecoratedLayer);
80 :
81 9 : GDALDataset *poDS = m_poCSVLayer->GetDataset();
82 18 : const CPLString osLayerName(m_poCSVLayer->GetName());
83 18 : const CPLString osFilename(m_poCSVLayer->GetFilename());
84 9 : const bool bCreateCSVT = m_poCSVLayer->GetCreateCSVT();
85 18 : const CPLString osCSVTFilename(CPLResetExtension(osFilename, "csvt"));
86 : VSIStatBufL sStatBuf;
87 9 : const bool bHasCSVT = VSIStatL(osCSVTFilename, &sStatBuf) == 0;
88 18 : CPLString osTmpFilename(osFilename);
89 18 : CPLString osTmpCSVTFilename(osFilename);
90 9 : if (VSIStatL(osFilename, &sStatBuf) == 0)
91 : {
92 9 : osTmpFilename += "_ogr_tmp.csv";
93 9 : osTmpCSVTFilename += "_ogr_tmp.csvt";
94 : }
95 9 : const char chDelimiter = m_poCSVLayer->GetDelimiter();
96 : OGRCSVLayer *poCSVTmpLayer = new OGRCSVLayer(
97 9 : poDS, osLayerName, nullptr, -1, osTmpFilename, true, true, chDelimiter);
98 9 : poCSVTmpLayer->BuildFeatureDefn(nullptr, nullptr, m_papszOpenOptions);
99 9 : poCSVTmpLayer->SetCRLF(m_poCSVLayer->GetCRLF());
100 9 : poCSVTmpLayer->SetCreateCSVT(bCreateCSVT || bHasCSVT);
101 9 : poCSVTmpLayer->SetWriteBOM(m_poCSVLayer->GetWriteBOM());
102 9 : poCSVTmpLayer->SetStringQuoting(m_poCSVLayer->GetStringQuoting());
103 :
104 9 : if (m_poCSVLayer->GetGeometryFormat() == OGR_CSV_GEOM_AS_WKT)
105 5 : poCSVTmpLayer->SetWriteGeometry(wkbNone, OGR_CSV_GEOM_AS_WKT, nullptr);
106 :
107 : const bool bKeepGeomColmuns =
108 9 : CPLFetchBool(m_papszOpenOptions, "KEEP_GEOM_COLUMNS", true);
109 :
110 9 : OGRErr eErr = OGRERR_NONE;
111 9 : OGRFeatureDefn *poEditableFDefn = poEditableLayer->GetLayerDefn();
112 31 : for (int i = 0; eErr == OGRERR_NONE && i < poEditableFDefn->GetFieldCount();
113 : i++)
114 : {
115 44 : OGRFieldDefn oFieldDefn(poEditableFDefn->GetFieldDefn(i));
116 22 : int iGeomFieldIdx = 0;
117 70 : if ((EQUAL(oFieldDefn.GetNameRef(), "WKT") &&
118 38 : (iGeomFieldIdx = poEditableFDefn->GetGeomFieldIndex("")) >= 0) ||
119 32 : (bKeepGeomColmuns &&
120 16 : (iGeomFieldIdx = poEditableFDefn->GetGeomFieldIndex(
121 38 : (std::string("geom_") + oFieldDefn.GetNameRef()).c_str())) >=
122 : 0))
123 : {
124 : OGRGeomFieldDefn oGeomFieldDefn(
125 5 : poEditableFDefn->GetGeomFieldDefn(iGeomFieldIdx));
126 5 : eErr = poCSVTmpLayer->CreateGeomField(&oGeomFieldDefn);
127 : }
128 : else
129 : {
130 17 : eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
131 : }
132 : }
133 :
134 12 : const bool bHasXY = !m_poCSVLayer->GetXField().empty() &&
135 3 : !m_poCSVLayer->GetYField().empty();
136 9 : const bool bHasZ = !m_poCSVLayer->GetZField().empty();
137 9 : if (bHasXY && !bKeepGeomColmuns)
138 : {
139 4 : if (poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
140 4 : m_poCSVLayer->GetXField()) < 0)
141 : {
142 4 : OGRFieldDefn oFieldDefn(m_poCSVLayer->GetXField(), OFTReal);
143 2 : if (eErr == OGRERR_NONE)
144 2 : eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
145 : }
146 4 : if (poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
147 4 : m_poCSVLayer->GetYField()) < 0)
148 : {
149 4 : OGRFieldDefn oFieldDefn(m_poCSVLayer->GetYField(), OFTReal);
150 2 : if (eErr == OGRERR_NONE)
151 2 : eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
152 : }
153 3 : if (bHasZ && poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
154 1 : m_poCSVLayer->GetZField()) < 0)
155 : {
156 2 : OGRFieldDefn oFieldDefn(m_poCSVLayer->GetZField(), OFTReal);
157 1 : if (eErr == OGRERR_NONE)
158 1 : eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
159 : }
160 : }
161 :
162 9 : int nFirstGeomColIdx = 0;
163 9 : if (m_poCSVLayer->HasHiddenWKTColumn())
164 : {
165 2 : poCSVTmpLayer->SetWriteGeometry(
166 1 : poEditableFDefn->GetGeomFieldDefn(0)->GetType(),
167 : OGR_CSV_GEOM_AS_WKT,
168 1 : poEditableFDefn->GetGeomFieldDefn(0)->GetNameRef());
169 1 : nFirstGeomColIdx = 1;
170 : }
171 :
172 9 : if (!(poEditableFDefn->GetGeomFieldCount() == 1 && bHasXY))
173 : {
174 12 : for (int i = nFirstGeomColIdx;
175 12 : eErr == OGRERR_NONE && i < poEditableFDefn->GetGeomFieldCount();
176 : i++)
177 : {
178 : OGRGeomFieldDefn oGeomFieldDefn(
179 6 : poEditableFDefn->GetGeomFieldDefn(i));
180 12 : if (poCSVTmpLayer->GetLayerDefn()->GetGeomFieldIndex(
181 12 : oGeomFieldDefn.GetNameRef()) >= 0)
182 5 : continue;
183 1 : eErr = poCSVTmpLayer->CreateGeomField(&oGeomFieldDefn);
184 : }
185 : }
186 :
187 9 : poEditableLayer->ResetReading();
188 :
189 : // Disable all filters.
190 9 : const char *pszQueryStringConst = poEditableLayer->GetAttrQueryString();
191 : char *pszQueryStringBak =
192 9 : pszQueryStringConst ? CPLStrdup(pszQueryStringConst) : nullptr;
193 9 : poEditableLayer->SetAttributeFilter(nullptr);
194 :
195 9 : const int iFilterGeomIndexBak = poEditableLayer->GetGeomFieldFilter();
196 9 : OGRGeometry *poFilterGeomBak = poEditableLayer->GetSpatialFilter();
197 9 : if (poFilterGeomBak)
198 1 : poFilterGeomBak = poFilterGeomBak->clone();
199 9 : poEditableLayer->SetSpatialFilter(nullptr);
200 :
201 : auto aoMapSrcToTargetIdx =
202 : poCSVTmpLayer->GetLayerDefn()->ComputeMapForSetFrom(
203 18 : poEditableLayer->GetLayerDefn(), true);
204 9 : aoMapSrcToTargetIdx.push_back(
205 9 : -1); // add dummy entry to be sure that .data() is valid
206 :
207 21 : for (auto &&poFeature : poEditableLayer)
208 : {
209 12 : if (eErr != OGRERR_NONE)
210 0 : break;
211 : OGRFeature *poNewFeature =
212 12 : new OGRFeature(poCSVTmpLayer->GetLayerDefn());
213 12 : poNewFeature->SetFrom(poFeature.get(), aoMapSrcToTargetIdx.data(),
214 : true);
215 12 : if (bHasXY)
216 : {
217 3 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
218 6 : if (poGeom != nullptr &&
219 3 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
220 : {
221 3 : auto poPoint = poGeom->toPoint();
222 3 : poNewFeature->SetField(m_poCSVLayer->GetXField(),
223 : poPoint->getX());
224 3 : poNewFeature->SetField(m_poCSVLayer->GetYField(),
225 : poPoint->getY());
226 3 : if (bHasZ)
227 : {
228 1 : poNewFeature->SetField(m_poCSVLayer->GetZField(),
229 : poPoint->getZ());
230 : }
231 : }
232 : }
233 12 : eErr = poCSVTmpLayer->CreateFeature(poNewFeature);
234 12 : delete poNewFeature;
235 : }
236 9 : delete poCSVTmpLayer;
237 :
238 : // Restore filters.
239 9 : poEditableLayer->SetAttributeFilter(pszQueryStringBak);
240 9 : CPLFree(pszQueryStringBak);
241 9 : poEditableLayer->SetSpatialFilter(iFilterGeomIndexBak, poFilterGeomBak);
242 9 : delete poFilterGeomBak;
243 :
244 9 : if (eErr != OGRERR_NONE)
245 : {
246 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error while creating %s",
247 : osTmpFilename.c_str());
248 0 : VSIUnlink(osTmpFilename);
249 0 : VSIUnlink(CPLResetExtension(osTmpFilename, "csvt"));
250 0 : return eErr;
251 : }
252 :
253 9 : delete m_poCSVLayer;
254 :
255 9 : if (osFilename != osTmpFilename)
256 : {
257 9 : const CPLString osTmpOriFilename(osFilename + ".ogr_bak");
258 9 : const CPLString osTmpOriCSVTFilename(osCSVTFilename + ".ogr_bak");
259 18 : if (VSIRename(osFilename, osTmpOriFilename) != 0 ||
260 4 : (bHasCSVT &&
261 4 : VSIRename(osCSVTFilename, osTmpOriCSVTFilename) != 0) ||
262 22 : VSIRename(osTmpFilename, osFilename) != 0 ||
263 4 : (bHasCSVT && VSIRename(osTmpCSVTFilename, osCSVTFilename) != 0))
264 : {
265 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot rename files");
266 0 : *ppoDecoratedLayer = nullptr;
267 0 : m_poCSVLayer = nullptr;
268 0 : return OGRERR_FAILURE;
269 : }
270 9 : VSIUnlink(osTmpOriFilename);
271 9 : if (bHasCSVT)
272 4 : VSIUnlink(osTmpOriCSVTFilename);
273 : }
274 :
275 9 : VSILFILE *fp = VSIFOpenL(osFilename, "rb+");
276 9 : if (fp == nullptr)
277 : {
278 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen updated %s",
279 : osFilename.c_str());
280 0 : *ppoDecoratedLayer = nullptr;
281 0 : m_poCSVLayer = nullptr;
282 0 : return OGRERR_FAILURE;
283 : }
284 :
285 9 : m_poCSVLayer =
286 : new OGRCSVLayer(poDS, osLayerName, fp, -1, osFilename, false, /* new */
287 : true, /* update */
288 9 : chDelimiter);
289 9 : m_poCSVLayer->BuildFeatureDefn(nullptr, nullptr, m_papszOpenOptions);
290 9 : *ppoDecoratedLayer = m_poCSVLayer;
291 :
292 9 : return OGRERR_NONE;
293 : }
294 :
295 : /************************************************************************/
296 : /* OGRCSVEditableLayer */
297 : /************************************************************************/
298 :
299 : class OGRCSVEditableLayer final : public IOGRCSVLayer, public OGREditableLayer
300 : {
301 : std::set<CPLString> m_oSetFields;
302 :
303 : public:
304 : OGRCSVEditableLayer(OGRCSVLayer *poCSVLayer, CSLConstList papszOpenOptions);
305 :
306 326 : OGRLayer *GetLayer() override
307 : {
308 326 : return this;
309 : }
310 :
311 18 : std::vector<std::string> GetFileList() override
312 : {
313 : return cpl::down_cast<OGRCSVEditableLayerSynchronizer *>(
314 : m_poSynchronizer)
315 18 : ->GetFileList();
316 : }
317 :
318 : virtual OGRErr CreateField(const OGRFieldDefn *poField,
319 : int bApproxOK = TRUE) override;
320 : virtual OGRErr DeleteField(int iField) override;
321 : virtual OGRErr AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
322 : int nFlagsIn) override;
323 : virtual GIntBig GetFeatureCount(int bForce = TRUE) override;
324 : };
325 :
326 : /************************************************************************/
327 : /* GRCSVEditableLayer() */
328 : /************************************************************************/
329 :
330 208 : OGRCSVEditableLayer::OGRCSVEditableLayer(OGRCSVLayer *poCSVLayer,
331 208 : CSLConstList papszOpenOptions)
332 : : OGREditableLayer(
333 : poCSVLayer, true,
334 208 : new OGRCSVEditableLayerSynchronizer(poCSVLayer, papszOpenOptions),
335 416 : true)
336 : {
337 208 : SetSupportsCreateGeomField(true);
338 208 : SetSupportsCurveGeometries(true);
339 208 : }
340 :
341 : /************************************************************************/
342 : /* CreateField() */
343 : /************************************************************************/
344 :
345 399 : OGRErr OGRCSVEditableLayer::CreateField(const OGRFieldDefn *poNewField,
346 : int bApproxOK)
347 :
348 : {
349 399 : if (m_poEditableFeatureDefn->GetFieldCount() >= 10000)
350 : {
351 0 : CPLError(CE_Failure, CPLE_AppDefined, "Limiting to 10000 fields");
352 0 : return OGRERR_FAILURE;
353 : }
354 :
355 399 : if (m_oSetFields.empty())
356 : {
357 101 : for (int i = 0; i < m_poEditableFeatureDefn->GetFieldCount(); i++)
358 : {
359 : m_oSetFields.insert(
360 8 : CPLString(
361 4 : m_poEditableFeatureDefn->GetFieldDefn(i)->GetNameRef())
362 8 : .toupper());
363 : }
364 : }
365 :
366 798 : const OGRCSVCreateFieldAction eAction = OGRCSVLayer::PreCreateField(
367 399 : m_poEditableFeatureDefn, m_oSetFields, poNewField, bApproxOK);
368 399 : if (eAction == CREATE_FIELD_DO_NOTHING)
369 0 : return OGRERR_NONE;
370 399 : if (eAction == CREATE_FIELD_ERROR)
371 1 : return OGRERR_FAILURE;
372 398 : OGRErr eErr = OGREditableLayer::CreateField(poNewField, bApproxOK);
373 398 : if (eErr == OGRERR_NONE)
374 : {
375 398 : m_oSetFields.insert(CPLString(poNewField->GetNameRef()).toupper());
376 : }
377 398 : return eErr;
378 : }
379 :
380 3 : OGRErr OGRCSVEditableLayer::DeleteField(int iField)
381 : {
382 3 : m_oSetFields.clear();
383 3 : return OGREditableLayer::DeleteField(iField);
384 : }
385 :
386 2 : OGRErr OGRCSVEditableLayer::AlterFieldDefn(int iField,
387 : OGRFieldDefn *poNewFieldDefn,
388 : int nFlagsIn)
389 : {
390 2 : m_oSetFields.clear();
391 2 : return OGREditableLayer::AlterFieldDefn(iField, poNewFieldDefn, nFlagsIn);
392 : }
393 :
394 : /************************************************************************/
395 : /* GetFeatureCount() */
396 : /************************************************************************/
397 :
398 28 : GIntBig OGRCSVEditableLayer::GetFeatureCount(int bForce)
399 : {
400 28 : const GIntBig nRet = OGREditableLayer::GetFeatureCount(bForce);
401 28 : if (m_poDecoratedLayer != nullptr && m_nNextFID <= 0)
402 : {
403 : const GIntBig nTotalFeatureCount =
404 8 : static_cast<OGRCSVLayer *>(m_poDecoratedLayer)
405 4 : ->GetTotalFeatureCount();
406 4 : if (nTotalFeatureCount >= 0)
407 3 : SetNextFID(nTotalFeatureCount + 1);
408 : }
409 28 : return nRet;
410 : }
411 :
412 : /************************************************************************/
413 : /* OGRCSVDataSource() */
414 : /************************************************************************/
415 :
416 : OGRCSVDataSource::OGRCSVDataSource() = default;
417 :
418 : /************************************************************************/
419 : /* ~OGRCSVDataSource() */
420 : /************************************************************************/
421 :
422 2392 : OGRCSVDataSource::~OGRCSVDataSource()
423 :
424 : {
425 1196 : m_apoLayers.clear();
426 :
427 1196 : if (bUpdate)
428 249 : OGRCSVDriverRemoveFromMap(pszName, this);
429 :
430 1196 : CPLFree(pszName);
431 2392 : }
432 :
433 : /************************************************************************/
434 : /* TestCapability() */
435 : /************************************************************************/
436 :
437 200 : int OGRCSVDataSource::TestCapability(const char *pszCap)
438 :
439 : {
440 200 : if (EQUAL(pszCap, ODsCCreateLayer))
441 65 : return bUpdate;
442 135 : else if (EQUAL(pszCap, ODsCDeleteLayer))
443 19 : return bUpdate;
444 116 : else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
445 30 : return bUpdate && bEnableGeometryFields;
446 86 : else if (EQUAL(pszCap, ODsCCurveGeometries))
447 3 : return TRUE;
448 83 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
449 3 : return TRUE;
450 80 : else if (EQUAL(pszCap, ODsCZGeometries))
451 2 : return TRUE;
452 78 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
453 0 : return bUpdate;
454 : else
455 78 : return FALSE;
456 : }
457 :
458 : /************************************************************************/
459 : /* GetLayer() */
460 : /************************************************************************/
461 :
462 873 : OGRLayer *OGRCSVDataSource::GetLayer(int iLayer)
463 :
464 : {
465 873 : if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
466 2 : return nullptr;
467 :
468 871 : return m_apoLayers[iLayer]->GetLayer();
469 : }
470 :
471 : /************************************************************************/
472 : /* GetRealExtension() */
473 : /************************************************************************/
474 :
475 7091 : CPLString OGRCSVDataSource::GetRealExtension(CPLString osFilename)
476 : {
477 14182 : const CPLString osExt = CPLGetExtension(osFilename);
478 7091 : if (STARTS_WITH(osFilename, "/vsigzip/") && EQUAL(osExt, "gz"))
479 : {
480 14 : if (osFilename.size() > 7 &&
481 7 : EQUAL(osFilename + osFilename.size() - 7, ".csv.gz"))
482 0 : return "csv";
483 14 : else if (osFilename.size() > 7 &&
484 7 : EQUAL(osFilename + osFilename.size() - 7, ".tsv.gz"))
485 0 : return "tsv";
486 14 : else if (osFilename.size() > 7 &&
487 7 : EQUAL(osFilename + osFilename.size() - 7, ".psv.gz"))
488 0 : return "psv";
489 : }
490 7091 : return osExt;
491 : }
492 :
493 : /************************************************************************/
494 : /* Open() */
495 : /************************************************************************/
496 :
497 1137 : bool OGRCSVDataSource::Open(const char *pszFilename, bool bUpdateIn,
498 : bool bForceOpen, CSLConstList papszOpenOptionsIn,
499 : bool bSingleDriver)
500 :
501 : {
502 1137 : pszName = CPLStrdup(pszFilename);
503 1137 : bUpdate = CPL_TO_BOOL(bUpdateIn);
504 :
505 1137 : if (bUpdate && bForceOpen && EQUAL(pszFilename, "/vsistdout/"))
506 1 : return TRUE;
507 :
508 : // For writable /vsizip/, do nothing more.
509 1136 : if (bUpdate && bForceOpen && STARTS_WITH(pszFilename, "/vsizip/"))
510 0 : return TRUE;
511 :
512 2272 : CPLString osFilename(pszFilename);
513 2272 : const CPLString osBaseFilename = CPLGetFilename(pszFilename);
514 2272 : const CPLString osExt = GetRealExtension(osFilename);
515 :
516 1136 : bool bIgnoreExtension = bSingleDriver;
517 1136 : bool bUSGeonamesFile = false;
518 1136 : if (STARTS_WITH_CI(osFilename, "CSV:"))
519 : {
520 0 : bIgnoreExtension = true;
521 0 : osFilename = osFilename.substr(strlen("CSV:"));
522 : }
523 :
524 : // Those are *not* real .XLS files, but text file with tab as column
525 : // separator.
526 1136 : if (EQUAL(osBaseFilename, "NfdcFacilities.xls") ||
527 1136 : EQUAL(osBaseFilename, "NfdcRunways.xls") ||
528 3408 : EQUAL(osBaseFilename, "NfdcRemarks.xls") ||
529 1136 : EQUAL(osBaseFilename, "NfdcSchedules.xls"))
530 : {
531 0 : if (bUpdate)
532 0 : return FALSE;
533 0 : bIgnoreExtension = true;
534 : }
535 1136 : else if ((STARTS_WITH_CI(osBaseFilename, "NationalFile_") ||
536 1136 : STARTS_WITH_CI(osBaseFilename, "POP_PLACES_") ||
537 1136 : STARTS_WITH_CI(osBaseFilename, "HIST_FEATURES_") ||
538 1136 : STARTS_WITH_CI(osBaseFilename, "US_CONCISE_") ||
539 1136 : STARTS_WITH_CI(osBaseFilename, "AllNames_") ||
540 1136 : STARTS_WITH_CI(osBaseFilename, "Feature_Description_History_") ||
541 1136 : STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
542 1136 : STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
543 1136 : STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
544 1136 : STARTS_WITH_CI(osBaseFilename, "AllStates_") ||
545 2272 : STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
546 1136 : (osBaseFilename.size() > 2 &&
547 2256 : STARTS_WITH_CI(osBaseFilename + 2, "_Features_")) ||
548 1136 : (osBaseFilename.size() > 2 &&
549 2272 : STARTS_WITH_CI(osBaseFilename + 2, "_FedCodes_"))) &&
550 0 : (EQUAL(osExt, "txt") || EQUAL(osExt, "zip")))
551 : {
552 0 : if (bUpdate)
553 0 : return FALSE;
554 0 : bIgnoreExtension = true;
555 0 : bUSGeonamesFile = true;
556 :
557 0 : if (EQUAL(osExt, "zip") && strstr(osFilename, "/vsizip/") == nullptr)
558 : {
559 0 : osFilename = "/vsizip/" + osFilename;
560 : }
561 : }
562 2271 : else if (EQUAL(osBaseFilename, "allCountries.txt") ||
563 1135 : EQUAL(osBaseFilename, "allCountries.zip"))
564 : {
565 1 : if (bUpdate)
566 0 : return FALSE;
567 1 : bIgnoreExtension = true;
568 :
569 1 : if (EQUAL(osExt, "zip") && strstr(osFilename, "/vsizip/") == nullptr)
570 : {
571 0 : osFilename = "/vsizip/" + osFilename;
572 : }
573 : }
574 :
575 : // Determine what sort of object this is.
576 : VSIStatBufL sStatBuf;
577 :
578 1136 : if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) != 0)
579 0 : return FALSE;
580 :
581 : // Is this a single CSV file?
582 1501 : if (VSI_ISREG(sStatBuf.st_mode) &&
583 365 : (bIgnoreExtension || EQUAL(osExt, "csv") || EQUAL(osExt, "tsv") ||
584 2 : EQUAL(osExt, "psv")))
585 : {
586 450 : if (EQUAL(CPLGetFilename(osFilename), "NfdcFacilities.xls"))
587 : {
588 0 : return OpenTable(osFilename, papszOpenOptionsIn, "ARP");
589 : }
590 450 : else if (EQUAL(CPLGetFilename(osFilename), "NfdcRunways.xls"))
591 : {
592 0 : OpenTable(osFilename, papszOpenOptionsIn, "BaseEndPhysical");
593 0 : OpenTable(osFilename, papszOpenOptionsIn, "BaseEndDisplaced");
594 0 : OpenTable(osFilename, papszOpenOptionsIn, "ReciprocalEndPhysical");
595 0 : OpenTable(osFilename, papszOpenOptionsIn, "ReciprocalEndDisplaced");
596 0 : return !m_apoLayers.empty();
597 : }
598 450 : else if (bUSGeonamesFile)
599 : {
600 : // GNIS specific.
601 0 : if (STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
602 0 : STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
603 0 : STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
604 0 : (osBaseFilename.size() > 2 &&
605 0 : STARTS_WITH_CI(osBaseFilename + 2, "_FedCodes_")))
606 : {
607 0 : OpenTable(osFilename, papszOpenOptionsIn, nullptr, "PRIMARY");
608 : }
609 0 : else if (STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
610 0 : STARTS_WITH_CI(osBaseFilename,
611 : "Feature_Description_History_"))
612 : {
613 0 : OpenTable(osFilename, papszOpenOptionsIn, nullptr, "");
614 : }
615 : else
616 : {
617 0 : OpenTable(osFilename, papszOpenOptionsIn, nullptr, "PRIM");
618 0 : OpenTable(osFilename, papszOpenOptionsIn, nullptr, "SOURCE");
619 : }
620 0 : return !m_apoLayers.empty();
621 : }
622 :
623 450 : return OpenTable(osFilename, papszOpenOptionsIn);
624 : }
625 :
626 : // Is this a single a ZIP file with only a CSV file inside?
627 690 : if (STARTS_WITH(osFilename, "/vsizip/") && EQUAL(osExt, "zip") &&
628 4 : VSI_ISREG(sStatBuf.st_mode))
629 : {
630 1 : char **papszFiles = VSIReadDir(osFilename);
631 2 : if (CSLCount(papszFiles) != 1 ||
632 1 : !EQUAL(CPLGetExtension(papszFiles[0]), "CSV"))
633 : {
634 1 : CSLDestroy(papszFiles);
635 1 : return FALSE;
636 : }
637 0 : osFilename = CPLFormFilename(osFilename, papszFiles[0], nullptr);
638 0 : CSLDestroy(papszFiles);
639 0 : return OpenTable(osFilename, papszOpenOptionsIn);
640 : }
641 :
642 : // Otherwise it has to be a directory.
643 685 : if (!VSI_ISDIR(sStatBuf.st_mode))
644 0 : return FALSE;
645 :
646 : // Scan through for entries ending in .csv.
647 685 : int nNotCSVCount = 0;
648 685 : char **papszNames = VSIReadDir(osFilename);
649 :
650 27824 : for (int i = 0; papszNames != nullptr && papszNames[i] != nullptr; i++)
651 : {
652 : const CPLString oSubFilename =
653 27139 : CPLFormFilename(osFilename, papszNames[i], nullptr);
654 :
655 27139 : if (EQUAL(papszNames[i], ".") || EQUAL(papszNames[i], ".."))
656 658 : continue;
657 :
658 26481 : if (EQUAL(CPLGetExtension(oSubFilename), "csvt"))
659 79 : continue;
660 :
661 52804 : if (VSIStatL(oSubFilename, &sStatBuf) != 0 ||
662 26402 : !VSI_ISREG(sStatBuf.st_mode))
663 : {
664 110 : nNotCSVCount++;
665 110 : continue;
666 : }
667 :
668 26292 : if (EQUAL(CPLGetExtension(oSubFilename), "csv"))
669 : {
670 139 : if (!OpenTable(oSubFilename, papszOpenOptionsIn))
671 : {
672 0 : CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
673 0 : nNotCSVCount++;
674 0 : continue;
675 : }
676 : }
677 : // GNIS specific.
678 78459 : else if (strlen(papszNames[i]) > 2 &&
679 26153 : STARTS_WITH_CI(papszNames[i] + 2, "_Features_") &&
680 0 : EQUAL(CPLGetExtension(papszNames[i]), "txt"))
681 : {
682 : bool bRet =
683 0 : OpenTable(oSubFilename, papszOpenOptionsIn, nullptr, "PRIM");
684 0 : bRet |=
685 0 : OpenTable(oSubFilename, papszOpenOptionsIn, nullptr, "SOURCE");
686 0 : if (!bRet)
687 : {
688 0 : CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
689 0 : nNotCSVCount++;
690 0 : continue;
691 : }
692 : }
693 : // GNIS specific.
694 104612 : else if (strlen(papszNames[i]) > 2 &&
695 26153 : STARTS_WITH_CI(papszNames[i] + 2, "_FedCodes_") &&
696 0 : EQUAL(CPLGetExtension(papszNames[i]), "txt"))
697 : {
698 0 : if (!OpenTable(oSubFilename, papszOpenOptionsIn, nullptr,
699 : "PRIMARY"))
700 : {
701 0 : CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
702 0 : nNotCSVCount++;
703 0 : continue;
704 : }
705 : }
706 : else
707 : {
708 26153 : nNotCSVCount++;
709 26153 : continue;
710 : }
711 : }
712 :
713 685 : CSLDestroy(papszNames);
714 :
715 : // We presume that this is indeed intended to be a CSV
716 : // datasource if over half the files were .csv files.
717 685 : return bForceOpen || nNotCSVCount < GetLayerCount();
718 : }
719 :
720 : /************************************************************************/
721 : /* OpenTable() */
722 : /************************************************************************/
723 :
724 589 : bool OGRCSVDataSource::OpenTable(const char *pszFilename,
725 : CSLConstList papszOpenOptionsIn,
726 : const char *pszNfdcRunwaysGeomField,
727 : const char *pszGeonamesGeomFieldPrefix)
728 :
729 : {
730 : // Open the file.
731 589 : VSILFILE *fp = nullptr;
732 :
733 589 : if (bUpdate)
734 88 : fp = VSIFOpenExL(pszFilename, "rb+", true);
735 : else
736 501 : fp = VSIFOpenExL(pszFilename, "rb", true);
737 589 : if (fp == nullptr)
738 : {
739 0 : CPLError(CE_Warning, CPLE_OpenFailed, "Failed to open %s.",
740 : VSIGetLastErrorMsg());
741 0 : return false;
742 : }
743 :
744 589 : if (!bUpdate && strstr(pszFilename, "/vsigzip/") == nullptr &&
745 501 : strstr(pszFilename, "/vsizip/") == nullptr)
746 417 : fp = VSICreateBufferedReaderHandle(fp);
747 :
748 1178 : CPLString osLayerName = CPLGetBasename(pszFilename);
749 1178 : CPLString osExt = CPLGetExtension(pszFilename);
750 589 : if (STARTS_WITH(pszFilename, "/vsigzip/") && EQUAL(osExt, "gz"))
751 : {
752 0 : if (strlen(pszFilename) > 7 &&
753 0 : EQUAL(pszFilename + strlen(pszFilename) - 7, ".csv.gz"))
754 : {
755 0 : osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
756 0 : osExt = "csv";
757 : }
758 0 : else if (strlen(pszFilename) > 7 &&
759 0 : EQUAL(pszFilename + strlen(pszFilename) - 7, ".tsv.gz"))
760 : {
761 0 : osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
762 0 : osExt = "tsv";
763 : }
764 0 : else if (strlen(pszFilename) > 7 &&
765 0 : EQUAL(pszFilename + strlen(pszFilename) - 7, ".psv.gz"))
766 : {
767 0 : osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
768 0 : osExt = "psv";
769 : }
770 : }
771 :
772 589 : int nMaxLineSize = atoi(CPLGetConfigOption(
773 : "OGR_CSV_MAX_LINE_SIZE",
774 : CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_LINE_SIZE",
775 589 : CPLSPrintf("%d", OGR_CSV_DEFAULT_MAX_LINE_SIZE))));
776 589 : size_t nMaxLineSizeAsSize_t = static_cast<size_t>(nMaxLineSize);
777 589 : if (nMaxLineSize == 0)
778 : {
779 0 : nMaxLineSize = -1;
780 0 : nMaxLineSizeAsSize_t = static_cast<size_t>(-1);
781 : }
782 :
783 : // Read and parse a line to detect separator.
784 :
785 1178 : std::string osLine;
786 : {
787 589 : const char *pszLine = CPLReadLine2L(fp, nMaxLineSize, nullptr);
788 589 : if (pszLine == nullptr)
789 : {
790 0 : VSIFCloseL(fp);
791 0 : return false;
792 : }
793 589 : osLine = pszLine;
794 : }
795 :
796 589 : char chDelimiter = ',';
797 : const char *pszDelimiter =
798 589 : CSLFetchNameValueDef(papszOpenOptionsIn, "SEPARATOR", "AUTO");
799 589 : if (EQUAL(pszDelimiter, "AUTO"))
800 : {
801 581 : chDelimiter = CSVDetectSeperator(osLine.c_str());
802 581 : if (chDelimiter != '\t' && osLine.find('\t') != std::string::npos)
803 : {
804 : // Force the delimiter to be TAB for a .tsv file that has a tabulation
805 : // in its first line */
806 0 : if (EQUAL(osExt, "tsv"))
807 : {
808 0 : chDelimiter = '\t';
809 : }
810 : else
811 : {
812 0 : for (int nDontHonourStrings = 0; nDontHonourStrings <= 1;
813 : nDontHonourStrings++)
814 : {
815 : const bool bHonourStrings =
816 0 : !CPL_TO_BOOL(nDontHonourStrings);
817 : // Read the first 2 lines to see if they have the same number
818 : // of fields, if using tabulation.
819 0 : VSIRewindL(fp);
820 0 : char **papszTokens = CSVReadParseLine3L(
821 : fp, nMaxLineSizeAsSize_t, "\t", bHonourStrings,
822 : false, // bKeepLeadingAndClosingQuotes
823 : false, // bMergeDelimiter
824 : true // bSkipBOM
825 : );
826 0 : const int nTokens1 = CSLCount(papszTokens);
827 0 : CSLDestroy(papszTokens);
828 0 : papszTokens = CSVReadParseLine3L(
829 : fp, nMaxLineSizeAsSize_t, "\t", bHonourStrings,
830 : false, // bKeepLeadingAndClosingQuotes
831 : false, // bMergeDelimiter
832 : true // bSkipBOM
833 : );
834 0 : const int nTokens2 = CSLCount(papszTokens);
835 0 : CSLDestroy(papszTokens);
836 0 : if (nTokens1 >= 2 && nTokens1 == nTokens2)
837 : {
838 0 : chDelimiter = '\t';
839 0 : break;
840 : }
841 : }
842 : }
843 : }
844 :
845 : // GNIS specific.
846 581 : if (pszGeonamesGeomFieldPrefix != nullptr &&
847 0 : osLine.find('|') != std::string::npos)
848 0 : chDelimiter = '|';
849 : }
850 8 : else if (EQUAL(pszDelimiter, "COMMA"))
851 4 : chDelimiter = ',';
852 4 : else if (EQUAL(pszDelimiter, "SEMICOLON"))
853 1 : chDelimiter = ';';
854 3 : else if (EQUAL(pszDelimiter, "TAB"))
855 1 : chDelimiter = '\t';
856 2 : else if (EQUAL(pszDelimiter, "SPACE"))
857 1 : chDelimiter = ' ';
858 1 : else if (EQUAL(pszDelimiter, "PIPE"))
859 1 : chDelimiter = '|';
860 : else
861 : {
862 0 : CPLError(CE_Warning, CPLE_AppDefined,
863 : "SEPARATOR=%s not understood, use one of COMMA, "
864 : "SEMICOLON, TAB, SPACE or PIPE",
865 : pszDelimiter);
866 : }
867 :
868 589 : VSIRewindL(fp);
869 :
870 : // Create a layer.
871 589 : if (pszNfdcRunwaysGeomField != nullptr)
872 : {
873 0 : osLayerName += "_";
874 0 : osLayerName += pszNfdcRunwaysGeomField;
875 : }
876 589 : else if (pszGeonamesGeomFieldPrefix != nullptr &&
877 0 : !EQUAL(pszGeonamesGeomFieldPrefix, ""))
878 : {
879 0 : osLayerName += "_";
880 0 : osLayerName += pszGeonamesGeomFieldPrefix;
881 : }
882 589 : if (EQUAL(pszFilename, "/vsistdin/"))
883 0 : osLayerName = "layer";
884 :
885 : auto poCSVLayer =
886 : std::make_unique<OGRCSVLayer>(this, osLayerName, fp, nMaxLineSize,
887 589 : pszFilename, FALSE, bUpdate, chDelimiter);
888 589 : poCSVLayer->BuildFeatureDefn(pszNfdcRunwaysGeomField,
889 : pszGeonamesGeomFieldPrefix,
890 : papszOpenOptionsIn);
891 589 : if (bUpdate)
892 : {
893 88 : m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
894 176 : poCSVLayer.release(), papszOpenOptionsIn));
895 : }
896 : else
897 : {
898 501 : m_apoLayers.emplace_back(std::move(poCSVLayer));
899 : }
900 :
901 589 : return true;
902 : }
903 :
904 : /************************************************************************/
905 : /* ICreateLayer() */
906 : /************************************************************************/
907 :
908 : OGRLayer *
909 125 : OGRCSVDataSource::ICreateLayer(const char *pszLayerName,
910 : const OGRGeomFieldDefn *poSrcGeomFieldDefn,
911 : CSLConstList papszOptions)
912 : {
913 : // Verify we are in update mode.
914 125 : if (!bUpdate)
915 : {
916 1 : CPLError(CE_Failure, CPLE_NoWriteAccess,
917 : "Data source %s opened read-only.\n"
918 : "New layer %s cannot be created.",
919 : pszName, pszLayerName);
920 :
921 1 : return nullptr;
922 : }
923 :
924 : const auto eGType =
925 124 : poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
926 : const auto poSpatialRef =
927 124 : poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
928 :
929 : // Verify that the datasource is a directory.
930 : VSIStatBufL sStatBuf;
931 :
932 124 : if (STARTS_WITH(pszName, "/vsizip/"))
933 : {
934 : // Do nothing.
935 : }
936 247 : else if (!EQUAL(pszName, "/vsistdout/") &&
937 123 : (VSIStatL(pszName, &sStatBuf) != 0 ||
938 123 : !VSI_ISDIR(sStatBuf.st_mode)))
939 : {
940 0 : CPLError(CE_Failure, CPLE_AppDefined,
941 : "Attempt to create csv layer (file) against a "
942 : "non-directory datasource.");
943 0 : return nullptr;
944 : }
945 :
946 : const bool bCreateCSVT =
947 124 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "CREATE_CSVT", "NO"));
948 :
949 : // What filename would we use?
950 248 : CPLString osFilename;
951 :
952 124 : if (strcmp(pszName, "/vsistdout/") == 0)
953 : {
954 1 : osFilename = pszName;
955 1 : if (bCreateCSVT)
956 : {
957 0 : CPLError(CE_Failure, CPLE_AppDefined,
958 : "CREATE_CSVT is not compatible with /vsistdout/ output");
959 0 : return nullptr;
960 : }
961 : }
962 123 : else if (osDefaultCSVName != "")
963 : {
964 59 : osFilename = CPLFormFilename(pszName, osDefaultCSVName, nullptr);
965 59 : osDefaultCSVName = "";
966 : }
967 : else
968 : {
969 64 : osFilename = CPLFormFilename(pszName, pszLayerName, "csv");
970 : }
971 :
972 : // Does this directory/file already exist?
973 124 : if (VSIStatL(osFilename, &sStatBuf) == 0)
974 : {
975 1 : CPLError(CE_Failure, CPLE_AppDefined,
976 : "Attempt to create layer %s, but %s already exists.",
977 : pszLayerName, osFilename.c_str());
978 1 : return nullptr;
979 : }
980 :
981 : // Create the empty file.
982 :
983 123 : const char *pszDelimiter = CSLFetchNameValue(papszOptions, "SEPARATOR");
984 123 : char chDelimiter = ',';
985 123 : if (pszDelimiter != nullptr)
986 : {
987 2 : if (EQUAL(pszDelimiter, "COMMA"))
988 0 : chDelimiter = ',';
989 2 : else if (EQUAL(pszDelimiter, "SEMICOLON"))
990 1 : chDelimiter = ';';
991 1 : else if (EQUAL(pszDelimiter, "TAB"))
992 0 : chDelimiter = '\t';
993 1 : else if (EQUAL(pszDelimiter, "SPACE"))
994 1 : chDelimiter = ' ';
995 : else
996 : {
997 0 : CPLError(CE_Warning, CPLE_AppDefined,
998 : "SEPARATOR=%s not understood, use one of "
999 : "COMMA, SEMICOLON, SPACE or TAB.",
1000 : pszDelimiter);
1001 : }
1002 : }
1003 :
1004 : // Create a layer.
1005 :
1006 : auto poCSVLayer = std::make_unique<OGRCSVLayer>(
1007 246 : this, pszLayerName, nullptr, -1, osFilename, true, true, chDelimiter);
1008 :
1009 123 : poCSVLayer->BuildFeatureDefn();
1010 :
1011 : // Was a particular CRLF order requested?
1012 123 : const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
1013 123 : bool bUseCRLF = false;
1014 :
1015 123 : if (pszCRLFFormat == nullptr)
1016 : {
1017 : #ifdef _WIN32
1018 : bUseCRLF = true;
1019 : #endif
1020 : }
1021 18 : else if (EQUAL(pszCRLFFormat, "CRLF"))
1022 : {
1023 1 : bUseCRLF = true;
1024 : }
1025 17 : else if (!EQUAL(pszCRLFFormat, "LF"))
1026 : {
1027 0 : CPLError(CE_Warning, CPLE_AppDefined,
1028 : "LINEFORMAT=%s not understood, use one of CRLF or LF.",
1029 : pszCRLFFormat);
1030 : #ifdef _WIN32
1031 : bUseCRLF = true;
1032 : #endif
1033 : }
1034 :
1035 123 : poCSVLayer->SetCRLF(bUseCRLF);
1036 :
1037 : const char *pszStringQuoting =
1038 123 : CSLFetchNameValueDef(papszOptions, "STRING_QUOTING", "IF_AMBIGUOUS");
1039 246 : poCSVLayer->SetStringQuoting(
1040 123 : EQUAL(pszStringQuoting, "IF_NEEDED")
1041 : ? OGRCSVLayer::StringQuoting::IF_NEEDED
1042 110 : : EQUAL(pszStringQuoting, "ALWAYS")
1043 110 : ? OGRCSVLayer::StringQuoting::ALWAYS
1044 : : OGRCSVLayer::StringQuoting::IF_AMBIGUOUS);
1045 :
1046 : // Should we write the geometry?
1047 123 : const char *pszGeometry = CSLFetchNameValue(papszOptions, "GEOMETRY");
1048 123 : if (bEnableGeometryFields)
1049 : {
1050 11 : poCSVLayer->SetWriteGeometry(
1051 : eGType, OGR_CSV_GEOM_AS_WKT,
1052 : CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "WKT"));
1053 : }
1054 112 : else if (pszGeometry != nullptr)
1055 : {
1056 35 : if (EQUAL(pszGeometry, "AS_WKT"))
1057 : {
1058 28 : poCSVLayer->SetWriteGeometry(
1059 : eGType, OGR_CSV_GEOM_AS_WKT,
1060 : CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "WKT"));
1061 : }
1062 7 : else if (EQUAL(pszGeometry, "AS_XYZ") || EQUAL(pszGeometry, "AS_XY") ||
1063 2 : EQUAL(pszGeometry, "AS_YX"))
1064 : {
1065 6 : if (eGType == wkbUnknown || wkbFlatten(eGType) == wkbPoint)
1066 : {
1067 10 : poCSVLayer->SetWriteGeometry(
1068 5 : eGType, EQUAL(pszGeometry, "AS_XYZ") ? OGR_CSV_GEOM_AS_XYZ
1069 3 : : EQUAL(pszGeometry, "AS_XY") ? OGR_CSV_GEOM_AS_XY
1070 : : OGR_CSV_GEOM_AS_YX);
1071 : }
1072 : else
1073 : {
1074 1 : CPLError(CE_Failure, CPLE_AppDefined,
1075 : "Geometry type %s is not compatible with "
1076 : "GEOMETRY=%s.",
1077 : OGRGeometryTypeToName(eGType), pszGeometry);
1078 1 : return nullptr;
1079 : }
1080 : }
1081 : else
1082 : {
1083 1 : CPLError(CE_Failure, CPLE_AppDefined,
1084 : "Unsupported value %s for creation option GEOMETRY",
1085 : pszGeometry);
1086 1 : return nullptr;
1087 : }
1088 : }
1089 :
1090 : // Should we create a CSVT file?
1091 121 : if (bCreateCSVT)
1092 : {
1093 28 : poCSVLayer->SetCreateCSVT(true);
1094 :
1095 : // Create .prj file.
1096 28 : if (poSpatialRef != nullptr)
1097 : {
1098 7 : char *pszWKT = nullptr;
1099 7 : poSpatialRef->exportToWkt(&pszWKT);
1100 7 : if (pszWKT)
1101 : {
1102 : VSILFILE *fpPRJ =
1103 7 : VSIFOpenL(CPLResetExtension(osFilename, "prj"), "wb");
1104 7 : if (fpPRJ)
1105 : {
1106 7 : CPL_IGNORE_RET_VAL(VSIFPrintfL(fpPRJ, "%s\n", pszWKT));
1107 7 : VSIFCloseL(fpPRJ);
1108 : }
1109 7 : CPLFree(pszWKT);
1110 : }
1111 : }
1112 : }
1113 :
1114 : // Should we write a UTF8 BOM?
1115 121 : const char *pszWriteBOM = CSLFetchNameValue(papszOptions, "WRITE_BOM");
1116 121 : if (pszWriteBOM)
1117 2 : poCSVLayer->SetWriteBOM(CPLTestBool(pszWriteBOM));
1118 :
1119 121 : if (poCSVLayer->GetLayerDefn()->GetGeomFieldCount() > 0 &&
1120 : poSrcGeomFieldDefn)
1121 : {
1122 39 : auto poGeomFieldDefn = poCSVLayer->GetLayerDefn()->GetGeomFieldDefn(0);
1123 39 : poGeomFieldDefn->SetCoordinatePrecision(
1124 : poSrcGeomFieldDefn->GetCoordinatePrecision());
1125 : }
1126 :
1127 121 : if (osFilename != "/vsistdout/")
1128 120 : m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
1129 240 : poCSVLayer.release(), nullptr));
1130 : else
1131 1 : m_apoLayers.emplace_back(std::move(poCSVLayer));
1132 :
1133 121 : return m_apoLayers.back()->GetLayer();
1134 : }
1135 :
1136 : /************************************************************************/
1137 : /* DeleteLayer() */
1138 : /************************************************************************/
1139 :
1140 20 : OGRErr OGRCSVDataSource::DeleteLayer(int iLayer)
1141 :
1142 : {
1143 : // Verify we are in update mode.
1144 20 : if (!bUpdate)
1145 : {
1146 1 : CPLError(CE_Failure, CPLE_NoWriteAccess,
1147 : "Data source %s opened read-only.\n"
1148 : "Layer %d cannot be deleted.",
1149 : pszName, iLayer);
1150 :
1151 1 : return OGRERR_FAILURE;
1152 : }
1153 :
1154 19 : if (iLayer < 0 || iLayer >= GetLayerCount())
1155 : {
1156 2 : CPLError(CE_Failure, CPLE_AppDefined,
1157 : "Layer %d not in legal range of 0 to %d.", iLayer,
1158 2 : GetLayerCount() - 1);
1159 2 : return OGRERR_FAILURE;
1160 : }
1161 :
1162 34 : for (const auto &osFilename : m_apoLayers[iLayer]->GetFileList())
1163 : {
1164 17 : VSIUnlink(osFilename.c_str());
1165 : }
1166 :
1167 17 : m_apoLayers.erase(m_apoLayers.begin() + iLayer);
1168 :
1169 17 : return OGRERR_NONE;
1170 : }
1171 :
1172 : /************************************************************************/
1173 : /* CreateForSingleFile() */
1174 : /************************************************************************/
1175 :
1176 59 : void OGRCSVDataSource::CreateForSingleFile(const char *pszDirname,
1177 : const char *pszFilename)
1178 : {
1179 59 : pszName = CPLStrdup(pszDirname);
1180 59 : bUpdate = true;
1181 59 : osDefaultCSVName = CPLGetFilename(pszFilename);
1182 59 : }
1183 :
1184 : /************************************************************************/
1185 : /* GetFileList() */
1186 : /************************************************************************/
1187 :
1188 7 : char **OGRCSVDataSource::GetFileList()
1189 : {
1190 14 : CPLStringList oFileList;
1191 19 : for (auto &poLayer : m_apoLayers)
1192 : {
1193 35 : for (const auto &osFilename : poLayer->GetFileList())
1194 : {
1195 23 : oFileList.AddString(osFilename.c_str());
1196 : }
1197 : }
1198 14 : return oFileList.StealList();
1199 : }
|