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