Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: PDS 4 Driver; Planetary Data System Format
4 : * Purpose: Implementation of PDS4Dataset
5 : * Author: Even Rouault, even.rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2019, Hobu Inc
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "pds4dataset.h"
14 : #include "ogrvrtgeometrytypes.h"
15 :
16 : #include "ogr_p.h"
17 :
18 : #include <algorithm>
19 : #include <cassert>
20 :
21 : PDS4TableLayerInterface::~PDS4TableLayerInterface() = default;
22 :
23 : /************************************************************************/
24 : /* ==================================================================== */
25 : /* PDS4TableBaseLayer */
26 : /* ==================================================================== */
27 : /************************************************************************/
28 :
29 166 : PDS4TableBaseLayer::PDS4TableBaseLayer(PDS4Dataset *poDS, const char *pszName,
30 166 : const char *pszFilename, bool bUpdate)
31 : : m_bUpdate(bUpdate), m_poDS(poDS),
32 166 : m_poRawFeatureDefn(new OGRFeatureDefn(pszName)),
33 332 : m_poFeatureDefn(new OGRFeatureDefn(pszName)), m_osFilename(pszFilename)
34 : {
35 166 : m_poRawFeatureDefn->SetGeomType(wkbNone);
36 166 : m_poRawFeatureDefn->Reference();
37 166 : m_poFeatureDefn->SetGeomType(wkbNone);
38 166 : m_poFeatureDefn->Reference();
39 166 : SetDescription(pszName);
40 :
41 166 : m_bKeepGeomColmuns =
42 166 : CPLFetchBool(m_poDS->GetOpenOptions(), "KEEP_GEOM_COLUMNS", false);
43 166 : }
44 :
45 : /************************************************************************/
46 : /* SetSpatialRef() */
47 : /************************************************************************/
48 :
49 3 : void PDS4TableBaseLayer::SetSpatialRef(OGRSpatialReference *poSRS)
50 : {
51 3 : if (GetGeomType() != wkbNone)
52 : {
53 3 : GetLayerDefn()->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
54 : }
55 3 : }
56 :
57 : /************************************************************************/
58 : /* ~PDS4TableBaseLayer() */
59 : /************************************************************************/
60 :
61 166 : PDS4TableBaseLayer::~PDS4TableBaseLayer()
62 : {
63 166 : m_poFeatureDefn->Release();
64 166 : m_poRawFeatureDefn->Release();
65 166 : if (m_fp)
66 165 : VSIFCloseL(m_fp);
67 166 : }
68 :
69 : /************************************************************************/
70 : /* RenameFileTo() */
71 : /************************************************************************/
72 :
73 3 : bool PDS4TableBaseLayer::RenameFileTo(const char *pszNewName)
74 : {
75 3 : if (m_fp)
76 3 : VSIFCloseL(m_fp);
77 3 : m_fp = nullptr;
78 6 : CPLString osBackup(pszNewName);
79 3 : osBackup += ".bak";
80 3 : VSIRename(pszNewName, osBackup);
81 3 : bool bSuccess = VSIRename(m_osFilename, pszNewName) == 0;
82 3 : if (bSuccess)
83 : {
84 3 : m_fp = VSIFOpenL(pszNewName, "rb+");
85 3 : if (!m_fp)
86 : {
87 0 : VSIRename(osBackup, pszNewName);
88 0 : return false;
89 : }
90 :
91 3 : m_osFilename = pszNewName;
92 3 : VSIUnlink(osBackup);
93 3 : return true;
94 : }
95 : else
96 : {
97 0 : VSIRename(osBackup, pszNewName);
98 0 : return false;
99 : }
100 : }
101 :
102 : /************************************************************************/
103 : /* GetFileList() */
104 : /************************************************************************/
105 :
106 64 : char **PDS4TableBaseLayer::GetFileList() const
107 : {
108 64 : return CSLAddString(nullptr, GetFileName());
109 : }
110 :
111 : /************************************************************************/
112 : /* GetFeatureCount() */
113 : /************************************************************************/
114 :
115 106 : GIntBig PDS4TableBaseLayer::GetFeatureCount(int bForce)
116 : {
117 106 : if (m_poAttrQuery != nullptr || m_poFilterGeom != nullptr)
118 : {
119 6 : return OGRLayer::GetFeatureCount(bForce);
120 : }
121 100 : return m_nFeatureCount;
122 : }
123 :
124 : /************************************************************************/
125 : /* SetupGeomField() */
126 : /************************************************************************/
127 :
128 97 : void PDS4TableBaseLayer::SetupGeomField()
129 : {
130 97 : const char *const *papszOpenOptions = m_poDS->GetOpenOptions();
131 97 : const char *pszWKT = CSLFetchNameValue(papszOpenOptions, "WKT");
132 194 : if (pszWKT == nullptr &&
133 140 : (m_iWKT = m_poRawFeatureDefn->GetFieldIndex("WKT")) >= 0 &&
134 43 : m_poRawFeatureDefn->GetFieldDefn(m_iWKT)->GetType() == OFTString)
135 : {
136 43 : pszWKT = "WKT";
137 : }
138 : else
139 : {
140 54 : m_iWKT = -1;
141 : }
142 97 : if (pszWKT && !EQUAL(pszWKT, ""))
143 : {
144 43 : m_iWKT = m_poRawFeatureDefn->GetFieldIndex(pszWKT);
145 43 : if (m_iWKT < 0)
146 : {
147 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown field %s", pszWKT);
148 : }
149 43 : else if (m_poRawFeatureDefn->GetFieldDefn(m_iWKT)->GetType() !=
150 : OFTString)
151 : {
152 0 : CPLError(CE_Warning, CPLE_AppDefined,
153 : "The %s field should be of type String", pszWKT);
154 : }
155 : else
156 : {
157 43 : m_poFeatureDefn->SetGeomType(wkbUnknown);
158 : }
159 : }
160 : else
161 : {
162 54 : const char *pszLat = CSLFetchNameValue(papszOpenOptions, "LAT");
163 54 : const char *pszLong = CSLFetchNameValue(papszOpenOptions, "LONG");
164 54 : if (pszLat == nullptr && pszLong == nullptr &&
165 54 : (m_iLatField = m_poRawFeatureDefn->GetFieldIndex("Latitude")) >=
166 11 : 0 &&
167 11 : (m_iLongField = m_poRawFeatureDefn->GetFieldIndex("Longitude")) >=
168 11 : 0 &&
169 11 : m_poRawFeatureDefn->GetFieldDefn(m_iLatField)->GetType() ==
170 108 : OFTReal &&
171 11 : m_poRawFeatureDefn->GetFieldDefn(m_iLongField)->GetType() ==
172 : OFTReal)
173 : {
174 11 : pszLat = "Latitude";
175 11 : pszLong = "Longitude";
176 : }
177 : else
178 : {
179 43 : m_iLatField = -1;
180 43 : m_iLongField = -1;
181 : }
182 54 : if (pszLat && pszLong && !EQUAL(pszLat, "") && !EQUAL(pszLong, ""))
183 : {
184 11 : m_iLatField = m_poRawFeatureDefn->GetFieldIndex(pszLat);
185 11 : m_iLongField = m_poRawFeatureDefn->GetFieldIndex(pszLong);
186 11 : if (m_iLatField < 0)
187 : {
188 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown field %s",
189 : pszLat);
190 : }
191 11 : else if (m_poRawFeatureDefn->GetFieldDefn(m_iLatField)->GetType() !=
192 : OFTReal)
193 : {
194 0 : CPLError(CE_Warning, CPLE_AppDefined,
195 : "The %s field should be of type Real", pszLat);
196 0 : m_iLatField = -1;
197 : }
198 11 : if (m_iLongField < 0)
199 : {
200 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown field %s",
201 : pszLong);
202 : }
203 22 : else if (m_poRawFeatureDefn->GetFieldDefn(m_iLongField)
204 11 : ->GetType() != OFTReal)
205 : {
206 0 : CPLError(CE_Warning, CPLE_AppDefined,
207 : "The %s field should be of type Real", pszLong);
208 0 : m_iLongField = -1;
209 : }
210 11 : if (m_iLatField < 0 || m_iLongField < 0)
211 : {
212 0 : m_iLatField = -1;
213 0 : m_iLongField = -1;
214 : }
215 : else
216 : {
217 11 : const char *pszAlt = CSLFetchNameValue(papszOpenOptions, "ALT");
218 22 : if (pszAlt == nullptr &&
219 11 : (m_iAltField =
220 22 : m_poRawFeatureDefn->GetFieldIndex("Altitude")) >= 0 &&
221 11 : m_poRawFeatureDefn->GetFieldDefn(m_iAltField)->GetType() ==
222 : OFTReal)
223 : {
224 11 : pszAlt = "Altitude";
225 : }
226 : else
227 : {
228 0 : m_iAltField = -1;
229 : }
230 11 : if (pszAlt && !EQUAL(pszAlt, ""))
231 : {
232 11 : m_iAltField = m_poRawFeatureDefn->GetFieldIndex(pszAlt);
233 11 : if (m_iAltField < 0)
234 : {
235 0 : CPLError(CE_Warning, CPLE_AppDefined,
236 : "Unknown field %s", pszAlt);
237 : }
238 22 : else if (m_poRawFeatureDefn->GetFieldDefn(m_iAltField)
239 11 : ->GetType() != OFTReal)
240 : {
241 0 : CPLError(CE_Warning, CPLE_AppDefined,
242 : "The %s field should be of type Real", pszAlt);
243 0 : m_iAltField = -1;
244 : }
245 : }
246 11 : m_poFeatureDefn->SetGeomType(m_iAltField >= 0 ? wkbPoint25D
247 11 : : wkbPoint);
248 : }
249 : }
250 : }
251 :
252 948 : for (int i = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
253 : {
254 851 : if (!m_bKeepGeomColmuns && (i == m_iWKT || i == m_iLatField ||
255 797 : i == m_iLongField || i == m_iAltField))
256 : {
257 : // do nothing;
258 : }
259 : else
260 : {
261 775 : m_poFeatureDefn->AddFieldDefn(m_poRawFeatureDefn->GetFieldDefn(i));
262 : }
263 : }
264 97 : }
265 :
266 : /************************************************************************/
267 : /* AddGeometryFromFields() */
268 : /************************************************************************/
269 :
270 869 : OGRFeature *PDS4TableBaseLayer::AddGeometryFromFields(OGRFeature *poRawFeature)
271 : {
272 869 : OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
273 869 : poFeature->SetFID(poRawFeature->GetFID());
274 11793 : for (int i = 0, j = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
275 : {
276 10924 : if (!m_bKeepGeomColmuns && (i == m_iWKT || i == m_iLatField ||
277 10070 : i == m_iLongField || i == m_iAltField))
278 : {
279 : // do nothing;
280 : }
281 : else
282 : {
283 9272 : poFeature->SetField(j, poRawFeature->GetRawFieldRef(i));
284 9272 : j++;
285 : }
286 : }
287 :
288 869 : if (m_iWKT >= 0)
289 : {
290 455 : const char *pszWKT = poRawFeature->GetFieldAsString(m_iWKT);
291 455 : if (pszWKT && pszWKT[0] != '\0')
292 : {
293 439 : OGRGeometry *poGeom = nullptr;
294 439 : OGRGeometryFactory::createFromWkt(pszWKT, nullptr, &poGeom);
295 439 : if (poGeom)
296 : {
297 439 : poGeom->assignSpatialReference(GetSpatialRef());
298 439 : poFeature->SetGeometryDirectly(poGeom);
299 : }
300 : }
301 : }
302 399 : else if (m_iLatField >= 0 && m_iLongField >= 0 &&
303 1210 : poRawFeature->IsFieldSetAndNotNull(m_iLatField) &&
304 397 : poRawFeature->IsFieldSetAndNotNull(m_iLongField))
305 : {
306 397 : double dfLat = poRawFeature->GetFieldAsDouble(m_iLatField);
307 397 : double dfLong = poRawFeature->GetFieldAsDouble(m_iLongField);
308 : OGRPoint *poPoint;
309 397 : if (m_iAltField >= 0 && poRawFeature->IsFieldSetAndNotNull(m_iAltField))
310 : {
311 397 : double dfAlt = poRawFeature->GetFieldAsDouble(m_iAltField);
312 397 : poPoint = new OGRPoint(dfLong, dfLat, dfAlt);
313 : }
314 : else
315 : {
316 0 : poPoint = new OGRPoint(dfLong, dfLat);
317 : }
318 397 : poPoint->assignSpatialReference(GetSpatialRef());
319 397 : poFeature->SetGeometryDirectly(poPoint);
320 : }
321 869 : return poFeature;
322 : }
323 :
324 : /************************************************************************/
325 : /* AddFieldsFromGeometry() */
326 : /************************************************************************/
327 :
328 119 : OGRFeature *PDS4TableBaseLayer::AddFieldsFromGeometry(OGRFeature *poFeature)
329 : {
330 119 : OGRFeature *poRawFeature = new OGRFeature(m_poRawFeatureDefn);
331 938 : for (int i = 0, j = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
332 : {
333 819 : if (!m_bKeepGeomColmuns && (i == m_iWKT || i == m_iLatField ||
334 715 : i == m_iLongField || i == m_iAltField))
335 : {
336 : // do nothing;
337 : }
338 : else
339 : {
340 689 : poRawFeature->SetField(i, poFeature->GetRawFieldRef(j));
341 689 : j++;
342 : }
343 : }
344 :
345 119 : auto poGeom = poFeature->GetGeometryRef();
346 119 : if (poGeom)
347 : {
348 84 : if (m_iLongField >= 0 && m_iLatField >= 0 &&
349 12 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
350 : {
351 12 : auto poPoint = poGeom->toPoint();
352 12 : poRawFeature->SetField(m_iLongField, poPoint->getX());
353 12 : poRawFeature->SetField(m_iLatField, poPoint->getY());
354 12 : if (m_iAltField >= 0 && poGeom->getGeometryType() == wkbPoint25D)
355 : {
356 12 : poRawFeature->SetField(m_iAltField, poPoint->getZ());
357 : }
358 : }
359 60 : else if (m_iWKT >= 0)
360 : {
361 60 : char *pszWKT = nullptr;
362 60 : poGeom->exportToWkt(&pszWKT);
363 60 : if (pszWKT)
364 : {
365 60 : poRawFeature->SetField(m_iWKT, pszWKT);
366 : }
367 60 : CPLFree(pszWKT);
368 : }
369 : }
370 119 : return poRawFeature;
371 : }
372 :
373 : /************************************************************************/
374 : /* MarkHeaderDirty() */
375 : /************************************************************************/
376 :
377 338 : void PDS4TableBaseLayer::MarkHeaderDirty()
378 : {
379 338 : m_bDirtyHeader = true;
380 338 : m_poDS->MarkHeaderDirty();
381 338 : }
382 :
383 : /************************************************************************/
384 : /* RefreshFileAreaObservationalBeginningCommon() */
385 : /************************************************************************/
386 :
387 69 : CPLXMLNode *PDS4TableBaseLayer::RefreshFileAreaObservationalBeginningCommon(
388 : CPLXMLNode *psFAO, const CPLString &osPrefix, const char *pszTableEltName,
389 : CPLString &osDescription)
390 : {
391 69 : CPLXMLNode *psFile = CPLGetXMLNode(psFAO, (osPrefix + "File").c_str());
392 69 : CPLAssert(psFile);
393 : CPLXMLNode *psfile_size =
394 69 : CPLGetXMLNode(psFile, (osPrefix + "file_size").c_str());
395 69 : if (psfile_size)
396 : {
397 3 : CPLRemoveXMLChild(psFile, psfile_size);
398 3 : CPLDestroyXMLNode(psfile_size);
399 : }
400 :
401 69 : CPLXMLNode *psHeader = CPLGetXMLNode(psFAO, (osPrefix + "Header").c_str());
402 69 : if (psHeader)
403 : {
404 3 : CPLRemoveXMLChild(psFAO, psHeader);
405 3 : CPLDestroyXMLNode(psHeader);
406 : }
407 :
408 138 : CPLString osTableEltName(osPrefix + pszTableEltName);
409 69 : CPLXMLNode *psTable = CPLGetXMLNode(psFAO, osTableEltName);
410 138 : CPLString osName;
411 69 : CPLString osLocalIdentifier;
412 69 : if (psTable)
413 : {
414 4 : osName = CPLGetXMLValue(psTable, (osPrefix + "name").c_str(), "");
415 : osLocalIdentifier = CPLGetXMLValue(
416 4 : psTable, (osPrefix + "local_identifier").c_str(), "");
417 : osDescription =
418 4 : CPLGetXMLValue(psTable, (osPrefix + "description").c_str(), "");
419 4 : CPLRemoveXMLChild(psFAO, psTable);
420 4 : CPLDestroyXMLNode(psTable);
421 : }
422 :
423 : // Write Table_Delimited/Table_Character/Table_Binary
424 69 : psTable = CPLCreateXMLNode(psFAO, CXT_Element, osTableEltName);
425 69 : if (!osName.empty())
426 3 : CPLCreateXMLElementAndValue(psTable, (osPrefix + "name").c_str(),
427 : osName);
428 69 : if (osLocalIdentifier.empty())
429 : {
430 : // Make a valid NCName
431 69 : osLocalIdentifier = GetName();
432 69 : if (isdigit(static_cast<unsigned char>(osLocalIdentifier[0])))
433 : {
434 4 : osLocalIdentifier = '_' + osLocalIdentifier;
435 : }
436 760 : for (char &ch : osLocalIdentifier)
437 : {
438 691 : if (!isalnum(static_cast<unsigned char>(ch)) &&
439 76 : static_cast<unsigned>(ch) <= 127)
440 76 : ch = '_';
441 : }
442 : }
443 138 : CPLCreateXMLElementAndValue(
444 138 : psTable, (osPrefix + "local_identifier").c_str(), osLocalIdentifier);
445 :
446 : CPLXMLNode *psOffset =
447 69 : CPLCreateXMLElementAndValue(psTable, (osPrefix + "offset").c_str(),
448 : CPLSPrintf(CPL_FRMT_GUIB, m_nOffset));
449 69 : CPLAddXMLAttributeAndValue(psOffset, "unit", "byte");
450 :
451 138 : return psTable;
452 : }
453 :
454 : /************************************************************************/
455 : /* ParseLineEndingOption() */
456 : /************************************************************************/
457 :
458 63 : void PDS4TableBaseLayer::ParseLineEndingOption(CSLConstList papszOptions)
459 : {
460 : const char *pszLineEnding =
461 63 : CSLFetchNameValueDef(papszOptions, "LINE_ENDING", "CRLF");
462 63 : if (EQUAL(pszLineEnding, "CRLF"))
463 : {
464 59 : m_osLineEnding = "\r\n";
465 : }
466 4 : else if (EQUAL(pszLineEnding, "LF"))
467 : {
468 2 : m_osLineEnding = "\n";
469 : }
470 : else
471 : {
472 2 : m_osLineEnding = "\r\n";
473 2 : CPLError(CE_Warning, CPLE_AppDefined,
474 : "Unhandled value for LINE_ENDING");
475 : }
476 63 : }
477 :
478 : /************************************************************************/
479 : /* GetDataset() */
480 : /************************************************************************/
481 :
482 17 : GDALDataset *PDS4TableBaseLayer::GetDataset()
483 : {
484 17 : return m_poDS;
485 : }
486 :
487 : /************************************************************************/
488 : /* ==================================================================== */
489 : /* PDS4FixedWidthTable */
490 : /* ==================================================================== */
491 : /************************************************************************/
492 :
493 46 : PDS4FixedWidthTable::PDS4FixedWidthTable(PDS4Dataset *poDS, const char *pszName,
494 46 : const char *pszFilename, bool bUpdate)
495 46 : : PDS4TableBaseLayer(poDS, pszName, pszFilename, bUpdate)
496 : {
497 46 : }
498 :
499 : /************************************************************************/
500 : /* ResetReading() */
501 : /************************************************************************/
502 :
503 215 : void PDS4FixedWidthTable::ResetReading()
504 : {
505 215 : m_nFID = 1;
506 215 : }
507 :
508 : /************************************************************************/
509 : /* GetNextFeature() */
510 : /************************************************************************/
511 :
512 470 : OGRFeature *PDS4FixedWidthTable::GetNextFeature()
513 : {
514 : while (true)
515 : {
516 470 : auto poFeature = GetFeature(m_nFID);
517 470 : if (poFeature == nullptr)
518 : {
519 77 : return nullptr;
520 : }
521 393 : ++m_nFID;
522 :
523 896 : if ((m_poFilterGeom == nullptr ||
524 746 : FilterGeometry(poFeature->GetGeometryRef())) &&
525 353 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
526 : {
527 318 : return poFeature;
528 : }
529 75 : delete poFeature;
530 75 : }
531 : }
532 :
533 : /************************************************************************/
534 : /* TestCapability() */
535 : /************************************************************************/
536 :
537 191 : bool PDS4FixedWidthTable::TestCapability(const char *pszCap) const
538 : {
539 191 : if (EQUAL(pszCap, OLCRandomRead) || EQUAL(pszCap, OLCStringsAsUTF8) ||
540 165 : EQUAL(pszCap, OLCZGeometries))
541 : {
542 32 : return true;
543 : }
544 159 : if (EQUAL(pszCap, OLCFastFeatureCount))
545 : {
546 0 : return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
547 : }
548 159 : if (EQUAL(pszCap, OLCCreateField))
549 : {
550 79 : return m_poDS->GetAccess() == GA_Update && m_nFeatureCount == 0;
551 : }
552 80 : if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite))
553 : {
554 14 : return m_poDS->GetAccess() == GA_Update;
555 : }
556 66 : return false;
557 : }
558 :
559 : /************************************************************************/
560 : /* ISetFeature() */
561 : /************************************************************************/
562 :
563 23 : OGRErr PDS4FixedWidthTable::ISetFeature(OGRFeature *poFeature)
564 : {
565 23 : if (poFeature->GetFID() <= 0 || poFeature->GetFID() > m_nFeatureCount)
566 : {
567 1 : return OGRERR_NON_EXISTING_FEATURE;
568 : }
569 22 : if (m_poDS->GetAccess() != GA_Update)
570 : {
571 0 : CPLError(CE_Failure, CPLE_AppDefined,
572 : "Dataset opened in read-only mode");
573 0 : return OGRERR_FAILURE;
574 : }
575 22 : CPLAssert(static_cast<int>(m_osBuffer.size()) == m_nRecordSize);
576 22 : CPLAssert(m_nRecordSize > static_cast<int>(m_osLineEnding.size()));
577 :
578 22 : VSIFSeekL(m_fp, m_nOffset + (poFeature->GetFID() - 1) * m_nRecordSize,
579 : SEEK_SET);
580 22 : memset(&m_osBuffer[0], ' ', m_nRecordSize);
581 :
582 22 : OGRFeature *poRawFeature = AddFieldsFromGeometry(poFeature);
583 347 : for (int i = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
584 : {
585 325 : if (!poRawFeature->IsFieldSetAndNotNull(i))
586 : {
587 21 : continue;
588 : }
589 608 : CPLString osBuffer;
590 304 : const CPLString &osDT(m_aoFields[i].m_osDataType);
591 304 : const auto eType(m_poRawFeatureDefn->GetFieldDefn(i)->GetType());
592 304 : if (osDT == "ASCII_Real")
593 : {
594 456 : CPLString osFormat;
595 228 : osFormat.Printf("%%.%dg", m_aoFields[i].m_nLength - 2);
596 : osBuffer.Printf(osFormat.c_str(),
597 228 : poRawFeature->GetFieldAsDouble(i));
598 : }
599 143 : else if (osDT == "ASCII_Integer" ||
600 143 : osDT == "ASCII_NonNegative_Integer" || eType == OFTString)
601 : {
602 17 : osBuffer = poRawFeature->GetFieldAsString(i);
603 : }
604 59 : else if (osDT == "ASCII_Boolean")
605 : {
606 8 : osBuffer = poRawFeature->GetFieldAsInteger(i) == 1 ? "1" : "0";
607 : }
608 51 : else if (osDT == "IEEE754LSBDouble")
609 : {
610 5 : double dfVal = poRawFeature->GetFieldAsDouble(i);
611 5 : CPL_LSBPTR64(&dfVal);
612 5 : osBuffer.resize(sizeof(dfVal));
613 5 : memcpy(&osBuffer[0], &dfVal, sizeof(dfVal));
614 : }
615 46 : else if (osDT == "IEEE754MSBDouble")
616 : {
617 2 : double dfVal = poRawFeature->GetFieldAsDouble(i);
618 2 : CPL_MSBPTR64(&dfVal);
619 2 : osBuffer.resize(sizeof(dfVal));
620 2 : memcpy(&osBuffer[0], &dfVal, sizeof(dfVal));
621 : }
622 44 : else if (osDT == "IEEE754LSBSingle")
623 : {
624 2 : float fVal = static_cast<float>(poRawFeature->GetFieldAsDouble(i));
625 2 : CPL_LSBPTR32(&fVal);
626 2 : osBuffer.resize(sizeof(fVal));
627 2 : memcpy(&osBuffer[0], &fVal, sizeof(fVal));
628 : }
629 42 : else if (osDT == "IEEE754MSBSingle")
630 : {
631 2 : float fVal = static_cast<float>(poRawFeature->GetFieldAsDouble(i));
632 2 : CPL_MSBPTR32(&fVal);
633 2 : osBuffer.resize(sizeof(fVal));
634 2 : memcpy(&osBuffer[0], &fVal, sizeof(fVal));
635 : }
636 40 : else if (osDT == "SignedByte")
637 : {
638 2 : signed char bVal = static_cast<signed char>(std::max(
639 2 : -128, std::min(127, poRawFeature->GetFieldAsInteger(i))));
640 2 : osBuffer.resize(sizeof(bVal));
641 2 : memcpy(&osBuffer[0], &bVal, sizeof(bVal));
642 : }
643 38 : else if (osDT == "UnsignedByte")
644 : {
645 : GByte ubVal = static_cast<GByte>(
646 2 : std::max(0, std::min(255, poRawFeature->GetFieldAsInteger(i))));
647 2 : osBuffer.resize(sizeof(ubVal));
648 2 : memcpy(&osBuffer[0], &ubVal, sizeof(ubVal));
649 : }
650 36 : else if (osDT == "SignedLSB2")
651 : {
652 1 : GInt16 sVal = static_cast<GInt16>(std::max(
653 1 : -32768, std::min(32767, poRawFeature->GetFieldAsInteger(i))));
654 1 : CPL_LSBPTR16(&sVal);
655 1 : osBuffer.resize(sizeof(sVal));
656 1 : memcpy(&osBuffer[0], &sVal, sizeof(sVal));
657 : }
658 35 : else if (osDT == "SignedMSB2")
659 : {
660 1 : GInt16 sVal = static_cast<GInt16>(std::max(
661 1 : -32768, std::min(32767, poRawFeature->GetFieldAsInteger(i))));
662 1 : CPL_MSBPTR16(&sVal);
663 1 : osBuffer.resize(sizeof(sVal));
664 1 : memcpy(&osBuffer[0], &sVal, sizeof(sVal));
665 : }
666 34 : else if (osDT == "UnsignedLSB2")
667 : {
668 1 : GUInt16 usVal = static_cast<GUInt16>(std::max(
669 1 : 0, std::min(65535, poRawFeature->GetFieldAsInteger(i))));
670 1 : CPL_LSBPTR16(&usVal);
671 1 : osBuffer.resize(sizeof(usVal));
672 1 : memcpy(&osBuffer[0], &usVal, sizeof(usVal));
673 : }
674 33 : else if (osDT == "UnsignedMSB2")
675 : {
676 1 : GUInt16 usVal = static_cast<GUInt16>(std::max(
677 1 : 0, std::min(65535, poRawFeature->GetFieldAsInteger(i))));
678 1 : CPL_MSBPTR16(&usVal);
679 1 : osBuffer.resize(sizeof(usVal));
680 1 : memcpy(&osBuffer[0], &usVal, sizeof(usVal));
681 : }
682 32 : else if (osDT == "SignedLSB4")
683 : {
684 1 : GInt32 nVal = poRawFeature->GetFieldAsInteger(i);
685 1 : CPL_LSBPTR32(&nVal);
686 1 : osBuffer.resize(sizeof(nVal));
687 1 : memcpy(&osBuffer[0], &nVal, sizeof(nVal));
688 : }
689 31 : else if (osDT == "SignedMSB4")
690 : {
691 1 : GInt32 nVal = poRawFeature->GetFieldAsInteger(i);
692 1 : CPL_MSBPTR32(&nVal);
693 1 : osBuffer.resize(sizeof(nVal));
694 1 : memcpy(&osBuffer[0], &nVal, sizeof(nVal));
695 : }
696 30 : else if (osDT == "UnsignedLSB4")
697 : {
698 1 : GUInt32 nVal = static_cast<GUInt32>(
699 1 : std::max(0, poRawFeature->GetFieldAsInteger(i)));
700 1 : CPL_LSBPTR32(&nVal);
701 1 : osBuffer.resize(sizeof(nVal));
702 1 : memcpy(&osBuffer[0], &nVal, sizeof(nVal));
703 : }
704 29 : else if (osDT == "UnsignedMSB4")
705 : {
706 1 : GUInt32 nVal = static_cast<GUInt32>(
707 1 : std::max(0, poRawFeature->GetFieldAsInteger(i)));
708 1 : CPL_MSBPTR32(&nVal);
709 1 : osBuffer.resize(sizeof(nVal));
710 1 : memcpy(&osBuffer[0], &nVal, sizeof(nVal));
711 : }
712 28 : else if (osDT == "SignedLSB8")
713 : {
714 1 : GInt64 nVal = poRawFeature->GetFieldAsInteger64(i);
715 1 : CPL_LSBPTR64(&nVal);
716 1 : osBuffer.resize(sizeof(nVal));
717 1 : memcpy(&osBuffer[0], &nVal, sizeof(nVal));
718 : }
719 27 : else if (osDT == "SignedMSB8")
720 : {
721 1 : GInt64 nVal = poRawFeature->GetFieldAsInteger64(i);
722 1 : CPL_MSBPTR64(&nVal);
723 1 : osBuffer.resize(sizeof(nVal));
724 1 : memcpy(&osBuffer[0], &nVal, sizeof(nVal));
725 : }
726 26 : else if (osDT == "UnsignedLSB8")
727 : {
728 1 : GUInt64 nVal = static_cast<GUInt64>(std::max(
729 1 : static_cast<GIntBig>(0), poRawFeature->GetFieldAsInteger64(i)));
730 1 : CPL_LSBPTR64(&nVal);
731 1 : osBuffer.resize(sizeof(nVal));
732 1 : memcpy(&osBuffer[0], &nVal, sizeof(nVal));
733 : }
734 25 : else if (osDT == "UnsignedMSB8")
735 : {
736 1 : GUInt64 nVal = static_cast<GUInt64>(std::max(
737 1 : static_cast<GIntBig>(0), poRawFeature->GetFieldAsInteger64(i)));
738 1 : CPL_MSBPTR64(&nVal);
739 1 : osBuffer.resize(sizeof(nVal));
740 1 : memcpy(&osBuffer[0], &nVal, sizeof(nVal));
741 : }
742 40 : else if (osDT == "ASCII_Date_Time_YMD" ||
743 16 : osDT == "ASCII_Date_Time_YMD_UTC")
744 : {
745 : char *pszDateTime =
746 8 : OGRGetXMLDateTime(poRawFeature->GetRawFieldRef(i));
747 8 : osBuffer = pszDateTime;
748 8 : CPLFree(pszDateTime);
749 : }
750 16 : else if (osDT == "ASCII_Date_YMD")
751 : {
752 : int nYear, nMonth, nDay;
753 8 : poRawFeature->GetFieldAsDateTime(
754 : i, &nYear, &nMonth, &nDay, nullptr, nullptr,
755 : static_cast<float *>(nullptr), nullptr);
756 8 : osBuffer.Printf("%04d-%02d-%02d", nYear, nMonth, nDay);
757 : }
758 8 : else if (osDT == "ASCII_Time")
759 : {
760 : int nHour, nMin;
761 : float fSec;
762 8 : poRawFeature->GetFieldAsDateTime(i, nullptr, nullptr, nullptr,
763 : &nHour, &nMin, &fSec, nullptr);
764 8 : osBuffer.Printf("%02d:%02d:%05.3f", nHour, nMin, fSec);
765 : }
766 :
767 608 : if (!osBuffer.empty() &&
768 304 : osBuffer.size() <= static_cast<size_t>(m_aoFields[i].m_nLength))
769 : {
770 304 : memcpy(&m_osBuffer[m_aoFields[i].m_nOffset +
771 304 : m_aoFields[i].m_nLength - osBuffer.size()],
772 304 : &osBuffer[0], osBuffer.size());
773 : }
774 0 : else if (!osBuffer.empty())
775 : {
776 0 : if (eType == OFTString)
777 : {
778 0 : CPLError(CE_Warning, CPLE_AppDefined,
779 : "Value %s for field %s is too large. Truncating it",
780 : osBuffer.c_str(),
781 0 : m_poRawFeatureDefn->GetFieldDefn(i)->GetNameRef());
782 0 : memcpy(&m_osBuffer[m_aoFields[i].m_nOffset], osBuffer.data(),
783 0 : m_aoFields[i].m_nLength);
784 : }
785 : else
786 : {
787 0 : CPLError(CE_Warning, CPLE_AppDefined,
788 : "Value %s for field %s is too large. Omitting i",
789 : osBuffer.c_str(),
790 0 : m_poRawFeatureDefn->GetFieldDefn(i)->GetNameRef());
791 : }
792 : }
793 : }
794 22 : delete poRawFeature;
795 :
796 22 : if (!m_osLineEnding.empty())
797 : {
798 34 : memcpy(&m_osBuffer[m_osBuffer.size() - m_osLineEnding.size()],
799 17 : m_osLineEnding.data(), m_osLineEnding.size());
800 : }
801 :
802 22 : if (VSIFWriteL(&m_osBuffer[0], m_nRecordSize, 1, m_fp) != 1)
803 : {
804 0 : return OGRERR_FAILURE;
805 : }
806 :
807 22 : return OGRERR_NONE;
808 : }
809 :
810 : /************************************************************************/
811 : /* ICreateFeature() */
812 : /************************************************************************/
813 :
814 22 : OGRErr PDS4FixedWidthTable::ICreateFeature(OGRFeature *poFeature)
815 : {
816 22 : if (!m_bUpdate)
817 : {
818 0 : CPLError(CE_Failure, CPLE_NotSupported,
819 : "Operation not supported on read-only layer");
820 0 : return OGRERR_FAILURE;
821 : }
822 :
823 22 : m_nFeatureCount++;
824 22 : poFeature->SetFID(m_nFeatureCount);
825 22 : OGRErr eErr = ISetFeature(poFeature);
826 22 : if (eErr == OGRERR_NONE)
827 : {
828 22 : MarkHeaderDirty();
829 : }
830 : else
831 : {
832 0 : poFeature->SetFID(-1);
833 0 : m_nFeatureCount--;
834 : }
835 22 : return eErr;
836 : }
837 :
838 : /************************************************************************/
839 : /* GetFeature() */
840 : /************************************************************************/
841 :
842 497 : OGRFeature *PDS4FixedWidthTable::GetFeature(GIntBig nFID)
843 : {
844 497 : if (nFID <= 0 || nFID > m_nFeatureCount)
845 : {
846 87 : return nullptr;
847 : }
848 410 : VSIFSeekL(m_fp, m_nOffset + (nFID - 1) * m_nRecordSize, SEEK_SET);
849 410 : if (VSIFReadL(&m_osBuffer[0], m_nRecordSize, 1, m_fp) != 1)
850 : {
851 0 : return nullptr;
852 : }
853 410 : OGRFeature *poRawFeature = new OGRFeature(m_poRawFeatureDefn);
854 410 : poRawFeature->SetFID(nFID);
855 9448 : for (int i = 0; i < poRawFeature->GetFieldCount(); i++)
856 : {
857 9038 : CPLString osVal(m_osBuffer.substr(m_aoFields[i].m_nOffset,
858 18076 : m_aoFields[i].m_nLength));
859 :
860 9038 : const CPLString &osDT(m_aoFields[i].m_osDataType);
861 9038 : if (STARTS_WITH(osDT, "ASCII_") || STARTS_WITH(osDT, "UTF8_"))
862 : {
863 8779 : osVal.Trim();
864 8779 : if (osVal.empty())
865 : {
866 42 : continue;
867 : }
868 : }
869 :
870 8996 : if (osDT == "IEEE754LSBDouble")
871 : {
872 : double dfVal;
873 5 : CPLAssert(osVal.size() == sizeof(dfVal));
874 5 : memcpy(&dfVal, osVal.data(), sizeof(dfVal));
875 5 : CPL_LSBPTR64(&dfVal);
876 5 : poRawFeature->SetField(i, dfVal);
877 : }
878 8991 : else if (osDT == "IEEE754MSBDouble")
879 : {
880 : double dfVal;
881 2 : CPLAssert(osVal.size() == sizeof(dfVal));
882 2 : memcpy(&dfVal, osVal.data(), sizeof(dfVal));
883 2 : CPL_MSBPTR64(&dfVal);
884 2 : poRawFeature->SetField(i, dfVal);
885 : }
886 8989 : else if (osDT == "IEEE754LSBSingle")
887 : {
888 : float fVal;
889 2 : CPLAssert(osVal.size() == sizeof(fVal));
890 2 : memcpy(&fVal, osVal.data(), sizeof(fVal));
891 2 : CPL_LSBPTR32(&fVal);
892 2 : poRawFeature->SetField(i, fVal);
893 : }
894 8987 : else if (osDT == "IEEE754MSBSingle")
895 : {
896 : float fVal;
897 2 : CPLAssert(osVal.size() == sizeof(fVal));
898 2 : memcpy(&fVal, osVal.data(), sizeof(fVal));
899 2 : CPL_MSBPTR32(&fVal);
900 2 : poRawFeature->SetField(i, fVal);
901 : }
902 8985 : else if (osDT == "SignedByte")
903 : {
904 : signed char bVal;
905 2 : CPLAssert(osVal.size() == sizeof(bVal));
906 2 : memcpy(&bVal, osVal.data(), sizeof(bVal));
907 2 : poRawFeature->SetField(i, bVal);
908 : }
909 8983 : else if (osDT == "UnsignedByte")
910 : {
911 : GByte bVal;
912 2 : CPLAssert(osVal.size() == sizeof(bVal));
913 2 : memcpy(&bVal, osVal.data(), sizeof(bVal));
914 2 : poRawFeature->SetField(i, bVal);
915 : }
916 8981 : else if (osDT == "SignedLSB2")
917 : {
918 : GInt16 sVal;
919 1 : CPLAssert(osVal.size() == sizeof(sVal));
920 1 : memcpy(&sVal, osVal.data(), sizeof(sVal));
921 1 : CPL_LSBPTR16(&sVal);
922 1 : poRawFeature->SetField(i, sVal);
923 : }
924 8980 : else if (osDT == "SignedMSB2")
925 : {
926 : GInt16 sVal;
927 1 : CPLAssert(osVal.size() == sizeof(sVal));
928 1 : memcpy(&sVal, osVal.data(), sizeof(sVal));
929 1 : CPL_MSBPTR16(&sVal);
930 1 : poRawFeature->SetField(i, sVal);
931 : }
932 8979 : else if (osDT == "UnsignedLSB2")
933 : {
934 : GUInt16 usVal;
935 1 : CPLAssert(osVal.size() == sizeof(usVal));
936 1 : memcpy(&usVal, osVal.data(), sizeof(usVal));
937 1 : CPL_LSBPTR16(&usVal);
938 1 : poRawFeature->SetField(i, usVal);
939 : }
940 8978 : else if (osDT == "UnsignedMSB2")
941 : {
942 : GUInt16 usVal;
943 232 : CPLAssert(osVal.size() == sizeof(usVal));
944 232 : memcpy(&usVal, osVal.data(), sizeof(usVal));
945 232 : CPL_MSBPTR16(&usVal);
946 232 : poRawFeature->SetField(i, usVal);
947 : }
948 8746 : else if (osDT == "SignedLSB4")
949 : {
950 : GInt32 nVal;
951 1 : CPLAssert(osVal.size() == sizeof(nVal));
952 1 : memcpy(&nVal, osVal.data(), sizeof(nVal));
953 1 : CPL_LSBPTR32(&nVal);
954 1 : poRawFeature->SetField(i, nVal);
955 : }
956 8745 : else if (osDT == "SignedMSB4")
957 : {
958 : GInt32 nVal;
959 1 : CPLAssert(osVal.size() == sizeof(nVal));
960 1 : memcpy(&nVal, osVal.data(), sizeof(nVal));
961 1 : CPL_MSBPTR32(&nVal);
962 1 : poRawFeature->SetField(i, nVal);
963 : }
964 8744 : else if (osDT == "UnsignedLSB4")
965 : {
966 : GUInt32 nVal;
967 1 : CPLAssert(osVal.size() == sizeof(nVal));
968 1 : memcpy(&nVal, osVal.data(), sizeof(nVal));
969 1 : CPL_LSBPTR32(&nVal);
970 1 : poRawFeature->SetField(i, static_cast<GIntBig>(nVal));
971 : }
972 8743 : else if (osDT == "UnsignedMSB4")
973 : {
974 : GUInt32 nVal;
975 2 : CPLAssert(osVal.size() == sizeof(nVal));
976 2 : memcpy(&nVal, osVal.data(), sizeof(nVal));
977 2 : CPL_MSBPTR32(&nVal);
978 2 : poRawFeature->SetField(i, static_cast<GIntBig>(nVal));
979 : }
980 8741 : else if (osDT == "SignedLSB8")
981 : {
982 : GInt64 nVal;
983 1 : CPLAssert(osVal.size() == sizeof(nVal));
984 1 : memcpy(&nVal, osVal.data(), sizeof(nVal));
985 1 : CPL_LSBPTR64(&nVal);
986 1 : poRawFeature->SetField(i, nVal);
987 : }
988 8740 : else if (osDT == "SignedMSB8")
989 : {
990 : GInt64 nVal;
991 1 : CPLAssert(osVal.size() == sizeof(nVal));
992 1 : memcpy(&nVal, osVal.data(), sizeof(nVal));
993 1 : CPL_MSBPTR64(&nVal);
994 1 : poRawFeature->SetField(i, nVal);
995 : }
996 8739 : else if (osDT == "UnsignedLSB8")
997 : {
998 : GUInt64 nVal;
999 1 : CPLAssert(osVal.size() == sizeof(nVal));
1000 1 : memcpy(&nVal, osVal.data(), sizeof(nVal));
1001 1 : CPL_LSBPTR64(&nVal);
1002 1 : poRawFeature->SetField(i, static_cast<GIntBig>(nVal));
1003 : }
1004 8738 : else if (osDT == "UnsignedMSB8")
1005 : {
1006 : GUInt64 nVal;
1007 1 : CPLAssert(osVal.size() == sizeof(nVal));
1008 1 : memcpy(&nVal, osVal.data(), sizeof(nVal));
1009 1 : CPL_MSBPTR64(&nVal);
1010 1 : poRawFeature->SetField(i, static_cast<GIntBig>(nVal));
1011 : }
1012 8737 : else if (osDT == "ASCII_Boolean")
1013 : {
1014 9 : poRawFeature->SetField(
1015 9 : i, EQUAL(osVal, "t") || EQUAL(osVal, "1") ? 1 : 0);
1016 : }
1017 : else
1018 : {
1019 8728 : poRawFeature->SetField(i, osVal.c_str());
1020 : }
1021 : }
1022 410 : OGRFeature *poFeature = AddGeometryFromFields(poRawFeature);
1023 410 : delete poRawFeature;
1024 410 : return poFeature;
1025 : }
1026 :
1027 : /************************************************************************/
1028 : /* GetFieldTypeFromPDS4DataType() */
1029 : /************************************************************************/
1030 :
1031 851 : static OGRFieldType GetFieldTypeFromPDS4DataType(const char *pszDataType,
1032 : int nDTSize,
1033 : OGRFieldSubType &eSubType,
1034 : bool &error)
1035 : {
1036 851 : OGRFieldType eType = OFTString;
1037 851 : eSubType = OFSTNone;
1038 851 : error = false;
1039 851 : if (EQUAL(pszDataType, "ASCII_Boolean"))
1040 : {
1041 29 : eSubType = OFSTBoolean;
1042 29 : eType = OFTInteger;
1043 : }
1044 822 : else if (EQUAL(pszDataType, "ASCII_Date_Time_YMD") ||
1045 777 : EQUAL(pszDataType, "ASCII_Date_Time_YMD_UTC"))
1046 : {
1047 45 : eType = OFTDateTime;
1048 : }
1049 777 : else if (EQUAL(pszDataType, "ASCII_Date_YMD"))
1050 : {
1051 45 : eType = OFTDate;
1052 : }
1053 732 : else if (EQUAL(pszDataType, "ASCII_Integer") ||
1054 668 : EQUAL(pszDataType, "ASCII_NonNegative_Integer"))
1055 : {
1056 64 : eType = OFTInteger;
1057 : }
1058 668 : else if (EQUAL(pszDataType, "SignedByte") ||
1059 664 : EQUAL(pszDataType, "UnsignedByte"))
1060 : {
1061 9 : if (nDTSize != 1)
1062 0 : error = true;
1063 9 : eType = OFTInteger;
1064 : }
1065 659 : else if (EQUAL(pszDataType, "SignedLSB2") ||
1066 657 : EQUAL(pszDataType, "SignedMSB2"))
1067 : {
1068 4 : if (nDTSize != 2)
1069 0 : error = true;
1070 4 : eType = OFTInteger;
1071 4 : eSubType = OFSTInt16;
1072 : }
1073 655 : else if (EQUAL(pszDataType, "UnsignedLSB2") ||
1074 653 : EQUAL(pszDataType, "UnsignedMSB2"))
1075 : {
1076 236 : if (nDTSize != 2)
1077 0 : error = true;
1078 236 : eType = OFTInteger;
1079 : }
1080 419 : else if (EQUAL(pszDataType, "SignedLSB4") ||
1081 417 : EQUAL(pszDataType, "SignedMSB4"))
1082 : {
1083 4 : if (nDTSize != 4)
1084 0 : error = true;
1085 4 : eType = OFTInteger;
1086 : }
1087 415 : else if (EQUAL(pszDataType, "UnsignedLSB4") ||
1088 413 : EQUAL(pszDataType, "UnsignedMSB4"))
1089 : {
1090 6 : if (nDTSize != 4)
1091 0 : error = true;
1092 : // Use larger data type as > 2 billion values don't hold on signed int32
1093 6 : eType = OFTInteger64;
1094 : }
1095 409 : else if (EQUAL(pszDataType, "SignedLSB8") ||
1096 407 : EQUAL(pszDataType, "SignedMSB8"))
1097 : {
1098 4 : if (nDTSize != 8)
1099 0 : error = true;
1100 4 : eType = OFTInteger64;
1101 : }
1102 405 : else if (EQUAL(pszDataType, "UnsignedLSB8") ||
1103 403 : EQUAL(pszDataType, "UnsignedMSB8"))
1104 : {
1105 5 : if (nDTSize != 8)
1106 0 : error = true;
1107 : // Hope that we won't get value larger than > 2^63...
1108 5 : eType = OFTInteger64;
1109 : }
1110 400 : else if (EQUAL(pszDataType, "ASCII_Real"))
1111 : {
1112 240 : eType = OFTReal;
1113 : }
1114 160 : else if (EQUAL(pszDataType, "IEEE754LSBDouble") ||
1115 153 : EQUAL(pszDataType, "IEEE754MSBDouble"))
1116 : {
1117 12 : if (nDTSize != 8)
1118 0 : error = true;
1119 12 : eType = OFTReal;
1120 : }
1121 148 : else if (EQUAL(pszDataType, "IEEE754LSBSingle") ||
1122 144 : EQUAL(pszDataType, "IEEE754MSBSingle"))
1123 : {
1124 9 : if (nDTSize != 4)
1125 0 : error = true;
1126 9 : eType = OFTReal;
1127 9 : eSubType = OFSTFloat32;
1128 : }
1129 139 : else if (EQUAL(pszDataType, "ASCII_Time"))
1130 : {
1131 29 : eType = OFTTime;
1132 : }
1133 851 : return eType;
1134 : }
1135 :
1136 : /************************************************************************/
1137 : /* ReadTableDef() */
1138 : /************************************************************************/
1139 :
1140 33 : bool PDS4FixedWidthTable::ReadTableDef(const CPLXMLNode *psTable)
1141 : {
1142 33 : CPLAssert(m_fp == nullptr);
1143 33 : m_fp = VSIFOpenL(m_osFilename,
1144 33 : (m_poDS->GetAccess() == GA_ReadOnly) ? "rb" : "r+b");
1145 33 : if (!m_fp)
1146 : {
1147 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
1148 : m_osFilename.c_str());
1149 0 : return false;
1150 : }
1151 :
1152 33 : m_nOffset = static_cast<GUIntBig>(
1153 33 : CPLAtoGIntBig(CPLGetXMLValue(psTable, "offset", "0")));
1154 :
1155 33 : m_nFeatureCount = CPLAtoGIntBig(CPLGetXMLValue(psTable, "records", "-1"));
1156 :
1157 : const char *pszRecordDelimiter =
1158 33 : CPLGetXMLValue(psTable, "record_delimiter", "");
1159 33 : if (EQUAL(pszRecordDelimiter, "Carriage-Return Line-Feed"))
1160 20 : m_osLineEnding = "\r\n";
1161 13 : else if (EQUAL(pszRecordDelimiter, "Line-Feed"))
1162 2 : m_osLineEnding = "\n";
1163 11 : else if (EQUAL(pszRecordDelimiter, ""))
1164 : {
1165 11 : if (GetSubType() == "Character")
1166 : {
1167 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing record_delimiter");
1168 0 : return false;
1169 : }
1170 : }
1171 : else
1172 : {
1173 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid record_delimiter");
1174 0 : return false;
1175 : }
1176 :
1177 : const CPLXMLNode *psRecord =
1178 33 : CPLGetXMLNode(psTable, ("Record_" + GetSubType()).c_str());
1179 33 : if (!psRecord)
1180 : {
1181 0 : return false;
1182 : }
1183 33 : m_nRecordSize = atoi(CPLGetXMLValue(psRecord, "record_length", "0"));
1184 66 : if (m_nRecordSize <= static_cast<int>(m_osLineEnding.size()) ||
1185 33 : m_nRecordSize > 1000 * 1000)
1186 : {
1187 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid record_length");
1188 0 : return false;
1189 : }
1190 33 : m_osBuffer.resize(m_nRecordSize);
1191 33 : if (!ReadFields(psRecord, 0, ""))
1192 : {
1193 0 : return false;
1194 : }
1195 :
1196 33 : SetupGeomField();
1197 :
1198 33 : return true;
1199 : }
1200 :
1201 : /************************************************************************/
1202 : /* ReadFields() */
1203 : /************************************************************************/
1204 :
1205 264 : bool PDS4FixedWidthTable::ReadFields(const CPLXMLNode *psParent,
1206 : int nBaseOffset,
1207 : const CPLString &osSuffixFieldName)
1208 : {
1209 2367 : for (const CPLXMLNode *psIter = psParent->psChild; psIter;
1210 2103 : psIter = psIter->psNext)
1211 : {
1212 4206 : if (psIter->eType == CXT_Element &&
1213 4206 : strcmp(psIter->pszValue, ("Field_" + GetSubType()).c_str()) == 0)
1214 : {
1215 617 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
1216 617 : if (!pszName)
1217 : {
1218 0 : return false;
1219 : }
1220 : const char *pszLoc =
1221 617 : CPLGetXMLValue(psIter, "field_location", nullptr);
1222 617 : if (!pszLoc)
1223 : {
1224 0 : return false;
1225 : }
1226 : const char *pszDataType =
1227 617 : CPLGetXMLValue(psIter, "data_type", nullptr);
1228 617 : if (!pszDataType)
1229 : {
1230 0 : return false;
1231 : }
1232 : const char *pszFieldLength =
1233 617 : CPLGetXMLValue(psIter, "field_length", nullptr);
1234 617 : if (!pszFieldLength)
1235 : {
1236 0 : return false;
1237 : }
1238 617 : Field f;
1239 617 : f.m_nOffset =
1240 617 : nBaseOffset + atoi(pszLoc) - 1; // Location is 1-based
1241 617 : if (f.m_nOffset < 0 || f.m_nOffset >= m_nRecordSize)
1242 : {
1243 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid field_location");
1244 0 : return false;
1245 : }
1246 617 : f.m_nLength = atoi(pszFieldLength);
1247 1234 : if (f.m_nLength <= 0 ||
1248 1234 : f.m_nLength > m_nRecordSize -
1249 617 : static_cast<int>(m_osLineEnding.size()) -
1250 617 : f.m_nOffset)
1251 : {
1252 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid field_length");
1253 0 : return false;
1254 : }
1255 617 : f.m_osDataType = pszDataType;
1256 617 : f.m_osUnit = CPLGetXMLValue(psIter, "unit", "");
1257 617 : f.m_osDescription = CPLGetXMLValue(psIter, "description", "");
1258 :
1259 : const char *pszFieldFormat =
1260 617 : CPLGetXMLValue(psIter, "field_format", "");
1261 :
1262 : CPLXMLNode *psSpecialConstants = const_cast<CPLXMLNode *>(
1263 617 : CPLGetXMLNode(psIter, "Special_Constants"));
1264 617 : if (psSpecialConstants)
1265 : {
1266 9 : auto psNext = psSpecialConstants->psNext;
1267 9 : psSpecialConstants->psNext = nullptr;
1268 9 : char *pszXML = CPLSerializeXMLTree(psSpecialConstants);
1269 9 : psSpecialConstants->psNext = psNext;
1270 9 : if (pszXML)
1271 : {
1272 9 : f.m_osSpecialConstantsXML = pszXML;
1273 9 : CPLFree(pszXML);
1274 : }
1275 : }
1276 :
1277 617 : m_aoFields.push_back(f);
1278 :
1279 617 : OGRFieldSubType eSubType = OFSTNone;
1280 617 : bool error = false;
1281 617 : auto eType = GetFieldTypeFromPDS4DataType(pszDataType, f.m_nLength,
1282 : eSubType, error);
1283 617 : if (error)
1284 : {
1285 0 : CPLError(CE_Failure, CPLE_AppDefined,
1286 : "Inconsistent field_length w.r.t datatype");
1287 0 : return false;
1288 : }
1289 658 : if (STARTS_WITH(f.m_osDataType, "ASCII_") && eType == OFTInteger &&
1290 41 : f.m_nLength >= 10)
1291 : {
1292 22 : eType = OFTInteger64;
1293 : }
1294 617 : OGRFieldDefn oFieldDefn((pszName + osSuffixFieldName).c_str(),
1295 1234 : eType);
1296 617 : oFieldDefn.SetSubType(eSubType);
1297 904 : if (eType != OFTReal && (STARTS_WITH(f.m_osDataType, "ASCII_") ||
1298 287 : STARTS_WITH(f.m_osDataType, "UTF_8")))
1299 : {
1300 98 : oFieldDefn.SetWidth(f.m_nLength);
1301 : }
1302 519 : else if ((eType == OFTInteger || eType == OFTInteger64) &&
1303 268 : pszFieldFormat && pszFieldFormat[0] == '%' &&
1304 9 : pszFieldFormat[strlen(pszFieldFormat) - 1] == 'd')
1305 : {
1306 9 : oFieldDefn.SetWidth(atoi(pszFieldFormat + 1));
1307 : }
1308 617 : m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
1309 : }
1310 2972 : else if (psIter->eType == CXT_Element &&
1311 1486 : strcmp(psIter->pszValue,
1312 2972 : ("Group_Field_" + GetSubType()).c_str()) == 0)
1313 : {
1314 : const char *pszRepetitions =
1315 1 : CPLGetXMLValue(psIter, "repetitions", nullptr);
1316 1 : if (!pszRepetitions)
1317 : {
1318 0 : return false;
1319 : }
1320 : const char *pszGroupLocation =
1321 1 : CPLGetXMLValue(psIter, "group_location", nullptr);
1322 1 : if (!pszGroupLocation)
1323 : {
1324 0 : return false;
1325 : }
1326 : const char *pszGroupLength =
1327 1 : CPLGetXMLValue(psIter, "group_length", nullptr);
1328 1 : if (!pszGroupLength)
1329 : {
1330 0 : return false;
1331 : }
1332 1 : int nRepetitions = std::min(1000, atoi(pszRepetitions));
1333 1 : if (nRepetitions <= 0)
1334 : {
1335 0 : return false;
1336 : }
1337 1 : int nGroupOffset =
1338 1 : atoi(pszGroupLocation) - 1; // Location is 1-based
1339 1 : if (nGroupOffset < 0 || nGroupOffset >= m_nRecordSize)
1340 : {
1341 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid group_location");
1342 0 : return false;
1343 : }
1344 1 : int nGroupLength = atoi(pszGroupLength);
1345 2 : if (nGroupLength <= 0 ||
1346 2 : nGroupLength > m_nRecordSize -
1347 1 : static_cast<int>(m_osLineEnding.size()) -
1348 2 : nGroupOffset ||
1349 1 : (nGroupLength % nRepetitions) != 0)
1350 : {
1351 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid group_length");
1352 0 : return false;
1353 : }
1354 1 : int nGroupOneRepetitionLength = nGroupLength / nRepetitions;
1355 232 : for (int i = 0; i < nRepetitions; i++)
1356 : {
1357 462 : if (!ReadFields(
1358 231 : psIter, nGroupOffset + i * nGroupOneRepetitionLength,
1359 462 : osSuffixFieldName + "_" + CPLSPrintf("%d", i + 1)))
1360 : {
1361 0 : return false;
1362 : }
1363 : }
1364 : }
1365 : }
1366 264 : return true;
1367 : }
1368 :
1369 : /************************************************************************/
1370 : /* RefreshFileAreaObservational() */
1371 : /************************************************************************/
1372 :
1373 14 : void PDS4FixedWidthTable::RefreshFileAreaObservational(CPLXMLNode *psFAO)
1374 : {
1375 28 : CPLString osPrefix;
1376 14 : if (STARTS_WITH(psFAO->pszValue, "pds:"))
1377 0 : osPrefix = "pds:";
1378 :
1379 28 : CPLString osDescription;
1380 14 : CPLXMLNode *psTable = RefreshFileAreaObservationalBeginningCommon(
1381 28 : psFAO, osPrefix, ("Table_" + GetSubType()).c_str(), osDescription);
1382 :
1383 14 : CPLCreateXMLElementAndValue(psTable, (osPrefix + "records").c_str(),
1384 : CPLSPrintf(CPL_FRMT_GIB, m_nFeatureCount));
1385 14 : if (!osDescription.empty())
1386 0 : CPLCreateXMLElementAndValue(psTable, (osPrefix + "description").c_str(),
1387 : osDescription);
1388 14 : if (m_osLineEnding == "\r\n")
1389 : {
1390 8 : CPLCreateXMLElementAndValue(psTable,
1391 16 : (osPrefix + "record_delimiter").c_str(),
1392 : "Carriage-Return Line-Feed");
1393 : }
1394 6 : else if (m_osLineEnding == "\n")
1395 : {
1396 1 : CPLCreateXMLElementAndValue(
1397 2 : psTable, (osPrefix + "record_delimiter").c_str(), "Line-Feed");
1398 : }
1399 :
1400 : // Write Record_Character / Record_Binary
1401 14 : CPLXMLNode *psRecord = CPLCreateXMLNode(
1402 28 : psTable, CXT_Element, (osPrefix + "Record_" + GetSubType()).c_str());
1403 28 : CPLCreateXMLElementAndValue(
1404 28 : psRecord, (osPrefix + "fields").c_str(),
1405 14 : CPLSPrintf("%d", static_cast<int>(m_aoFields.size())));
1406 14 : CPLCreateXMLElementAndValue(psRecord, (osPrefix + "groups").c_str(), "0");
1407 28 : CPLXMLNode *psrecord_length = CPLCreateXMLElementAndValue(
1408 28 : psRecord, (osPrefix + "record_length").c_str(),
1409 : CPLSPrintf("%d", m_nRecordSize));
1410 14 : CPLAddXMLAttributeAndValue(psrecord_length, "unit", "byte");
1411 :
1412 14 : CPLAssert(static_cast<int>(m_aoFields.size()) ==
1413 : m_poRawFeatureDefn->GetFieldCount());
1414 :
1415 163 : for (int i = 0; i < static_cast<int>(m_aoFields.size()); i++)
1416 : {
1417 149 : auto &f = m_aoFields[i];
1418 149 : auto poFieldDefn = m_poRawFeatureDefn->GetFieldDefn(i);
1419 :
1420 : CPLXMLNode *psField =
1421 149 : CPLCreateXMLNode(psRecord, CXT_Element,
1422 298 : (osPrefix + "Field_" + GetSubType()).c_str());
1423 :
1424 149 : CPLCreateXMLElementAndValue(psField, (osPrefix + "name").c_str(),
1425 : poFieldDefn->GetNameRef());
1426 :
1427 298 : CPLCreateXMLElementAndValue(psField,
1428 298 : (osPrefix + "field_number").c_str(),
1429 : CPLSPrintf("%d", i + 1));
1430 :
1431 149 : auto psfield_location = CPLCreateXMLElementAndValue(
1432 298 : psField, (osPrefix + "field_location").c_str(),
1433 149 : CPLSPrintf("%d", f.m_nOffset + 1));
1434 149 : CPLAddXMLAttributeAndValue(psfield_location, "unit", "byte");
1435 :
1436 149 : CPLCreateXMLElementAndValue(psField, (osPrefix + "data_type").c_str(),
1437 : f.m_osDataType.c_str());
1438 :
1439 298 : auto psfield_length = CPLCreateXMLElementAndValue(
1440 298 : psField, (osPrefix + "field_length").c_str(),
1441 : CPLSPrintf("%d", f.m_nLength));
1442 149 : CPLAddXMLAttributeAndValue(psfield_length, "unit", "byte");
1443 :
1444 149 : const auto eType(poFieldDefn->GetType());
1445 149 : const int nWidth = poFieldDefn->GetWidth();
1446 149 : if ((eType == OFTInteger || eType == OFTInteger64) && nWidth > 0)
1447 : {
1448 8 : CPLCreateXMLElementAndValue(psField,
1449 8 : (osPrefix + "field_format").c_str(),
1450 : CPLSPrintf("%%%dd", nWidth));
1451 : }
1452 :
1453 149 : if (!f.m_osUnit.empty())
1454 : {
1455 30 : CPLCreateXMLElementAndValue(psField, (osPrefix + "unit").c_str(),
1456 30 : m_aoFields[i].m_osUnit.c_str());
1457 : }
1458 :
1459 149 : if (!f.m_osDescription.empty())
1460 : {
1461 132 : CPLCreateXMLElementAndValue(psField,
1462 132 : (osPrefix + "description").c_str(),
1463 66 : m_aoFields[i].m_osDescription.c_str());
1464 : }
1465 149 : if (!f.m_osSpecialConstantsXML.empty())
1466 : {
1467 : auto psSpecialConstants =
1468 3 : CPLParseXMLString(f.m_osSpecialConstantsXML);
1469 3 : if (psSpecialConstants)
1470 : {
1471 3 : CPLAddXMLChild(psField, psSpecialConstants);
1472 : }
1473 : }
1474 : }
1475 14 : }
1476 :
1477 : /************************************************************************/
1478 : /* CreateField() */
1479 : /************************************************************************/
1480 :
1481 115 : OGRErr PDS4FixedWidthTable::CreateField(const OGRFieldDefn *poFieldIn, int)
1482 :
1483 : {
1484 115 : if (m_poDS->GetAccess() != GA_Update)
1485 : {
1486 0 : CPLError(CE_Failure, CPLE_AppDefined,
1487 : "Dataset opened in read-only mode");
1488 0 : return OGRERR_FAILURE;
1489 : }
1490 115 : if (m_nFeatureCount > 0)
1491 : {
1492 0 : return OGRERR_FAILURE;
1493 : }
1494 :
1495 230 : Field f;
1496 115 : if (!m_aoFields.empty())
1497 : {
1498 106 : f.m_nOffset = m_aoFields.back().m_nOffset + m_aoFields.back().m_nLength;
1499 : }
1500 :
1501 115 : if (!CreateFieldInternal(poFieldIn->GetType(), poFieldIn->GetSubType(),
1502 115 : poFieldIn->GetWidth(), f))
1503 : {
1504 0 : return OGRERR_FAILURE;
1505 : }
1506 :
1507 115 : MarkHeaderDirty();
1508 115 : m_aoFields.push_back(f);
1509 115 : m_poRawFeatureDefn->AddFieldDefn(poFieldIn);
1510 115 : m_poFeatureDefn->AddFieldDefn(poFieldIn);
1511 115 : m_nRecordSize += f.m_nLength;
1512 115 : m_osBuffer.resize(m_nRecordSize);
1513 :
1514 115 : return OGRERR_NONE;
1515 : }
1516 :
1517 : /************************************************************************/
1518 : /* InitializeNewLayer() */
1519 : /************************************************************************/
1520 :
1521 13 : bool PDS4FixedWidthTable::InitializeNewLayer(const OGRSpatialReference *poSRS,
1522 : bool bForceGeographic,
1523 : OGRwkbGeometryType eGType,
1524 : const char *const *papszOptions)
1525 : {
1526 13 : CPLAssert(m_fp == nullptr);
1527 13 : m_fp = VSIFOpenL(m_osFilename, "wb+");
1528 13 : if (!m_fp)
1529 : {
1530 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s",
1531 : m_osFilename.c_str());
1532 0 : return false;
1533 : }
1534 13 : m_aosLCO.Assign(CSLDuplicate(papszOptions));
1535 :
1536 13 : m_nRecordSize = 0;
1537 :
1538 : const char *pszGeomColumns =
1539 13 : CSLFetchNameValueDef(papszOptions, "GEOM_COLUMNS", "AUTO");
1540 13 : if (EQUAL(pszGeomColumns, "WKT"))
1541 : {
1542 0 : CPLError(CE_Warning, CPLE_AppDefined,
1543 : "GEOM_COLUMNS=WKT only supported for delimited/CSV tables");
1544 : }
1545 :
1546 13 : if ((EQUAL(pszGeomColumns, "AUTO") && wkbFlatten(eGType) == wkbPoint &&
1547 26 : (bForceGeographic || (poSRS && poSRS->IsGeographic()))) ||
1548 9 : (EQUAL(pszGeomColumns, "LONG_LAT") && eGType != wkbNone))
1549 : {
1550 : {
1551 : OGRFieldDefn oFieldDefn(
1552 8 : CSLFetchNameValueDef(papszOptions, "LAT", "Latitude"), OFTReal);
1553 4 : m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
1554 4 : m_iLatField = m_poRawFeatureDefn->GetFieldCount() - 1;
1555 4 : Field f;
1556 4 : f.m_nOffset = m_aoFields.empty() ? 0
1557 0 : : m_aoFields.back().m_nOffset +
1558 0 : m_aoFields.back().m_nLength;
1559 4 : CreateFieldInternal(OFTReal, OFSTNone, 0, f);
1560 4 : m_aoFields.push_back(f);
1561 4 : m_nRecordSize += f.m_nLength;
1562 : }
1563 : {
1564 : OGRFieldDefn oFieldDefn(
1565 : CSLFetchNameValueDef(papszOptions, "LONG", "Longitude"),
1566 8 : OFTReal);
1567 4 : m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
1568 4 : m_iLongField = m_poRawFeatureDefn->GetFieldCount() - 1;
1569 4 : Field f;
1570 4 : f.m_nOffset =
1571 4 : m_aoFields.back().m_nOffset + m_aoFields.back().m_nLength;
1572 4 : CreateFieldInternal(OFTReal, OFSTNone, 0, f);
1573 4 : m_aoFields.push_back(f);
1574 4 : m_nRecordSize += f.m_nLength;
1575 : }
1576 4 : if (eGType == wkbPoint25D)
1577 : {
1578 : OGRFieldDefn oFieldDefn(
1579 8 : CSLFetchNameValueDef(papszOptions, "ALT", "Altitude"), OFTReal);
1580 4 : m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
1581 4 : m_iAltField = m_poRawFeatureDefn->GetFieldCount() - 1;
1582 4 : Field f;
1583 4 : f.m_nOffset =
1584 4 : m_aoFields.back().m_nOffset + m_aoFields.back().m_nLength;
1585 4 : CreateFieldInternal(OFTReal, OFSTNone, 0, f);
1586 4 : m_aoFields.push_back(f);
1587 4 : m_nRecordSize += f.m_nLength;
1588 : }
1589 :
1590 4 : m_poRawFeatureDefn->SetGeomType(eGType);
1591 :
1592 4 : m_poFeatureDefn->SetGeomType(eGType);
1593 4 : if (poSRS)
1594 : {
1595 2 : auto poSRSClone = poSRS->Clone();
1596 2 : poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1597 2 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRSClone);
1598 2 : poSRSClone->Release();
1599 : }
1600 : }
1601 :
1602 13 : if (GetSubType() == "Character")
1603 : {
1604 8 : ParseLineEndingOption(papszOptions);
1605 : }
1606 13 : m_nRecordSize += static_cast<int>(m_osLineEnding.size());
1607 13 : m_osBuffer.resize(m_nRecordSize);
1608 :
1609 13 : m_nFeatureCount = 0;
1610 13 : MarkHeaderDirty();
1611 13 : return true;
1612 : }
1613 :
1614 : /************************************************************************/
1615 : /* ==================================================================== */
1616 : /* PDS4TableCharacter */
1617 : /* ==================================================================== */
1618 : /************************************************************************/
1619 :
1620 30 : PDS4TableCharacter::PDS4TableCharacter(PDS4Dataset *poDS, const char *pszName,
1621 30 : const char *pszFilename, bool bUpdate)
1622 30 : : PDS4FixedWidthTable(poDS, pszName, pszFilename, bUpdate)
1623 : {
1624 30 : }
1625 :
1626 : /************************************************************************/
1627 : /* CreateFieldInternal() */
1628 : /************************************************************************/
1629 :
1630 80 : bool PDS4TableCharacter::CreateFieldInternal(OGRFieldType eType,
1631 : OGRFieldSubType eSubType,
1632 : int nWidth, Field &f)
1633 : {
1634 80 : if (nWidth > 0)
1635 : {
1636 0 : f.m_nLength = nWidth;
1637 : }
1638 : else
1639 : {
1640 80 : if (eType == OFTString)
1641 : {
1642 4 : f.m_nLength = 64;
1643 : }
1644 76 : else if (eType == OFTInteger)
1645 : {
1646 9 : f.m_nLength = eSubType == OFSTBoolean ? 1 : 11;
1647 : }
1648 67 : else if (eType == OFTInteger64)
1649 : {
1650 4 : f.m_nLength = 21;
1651 : }
1652 63 : else if (eType == OFTReal)
1653 : {
1654 51 : f.m_nLength = 16;
1655 : }
1656 12 : else if (eType == OFTDateTime)
1657 : {
1658 : // YYYY-MM-DDTHH:MM:SS.sssZ
1659 4 : f.m_nLength = 4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 3 + 1;
1660 : }
1661 8 : else if (eType == OFTDate)
1662 : {
1663 : // YYYY-MM-DD
1664 4 : f.m_nLength = 4 + 1 + 2 + 1 + 2;
1665 : }
1666 4 : else if (eType == OFTTime)
1667 : {
1668 : // HH:MM:SS.sss
1669 4 : f.m_nLength = 2 + 1 + 2 + 1 + 2 + 1 + 3;
1670 : }
1671 : }
1672 80 : if (eType == OFTString)
1673 : {
1674 4 : f.m_osDataType = "UTF8_String";
1675 : }
1676 76 : else if (eType == OFTInteger)
1677 : {
1678 : f.m_osDataType =
1679 9 : eSubType == OFSTBoolean ? "ASCII_Boolean" : "ASCII_Integer";
1680 : }
1681 67 : else if (eType == OFTInteger64)
1682 : {
1683 4 : f.m_osDataType = "ASCII_Integer";
1684 : }
1685 63 : else if (eType == OFTReal)
1686 : {
1687 51 : f.m_osDataType = "ASCII_Real";
1688 : }
1689 12 : else if (eType == OFTDateTime)
1690 : {
1691 4 : f.m_osDataType = "ASCII_Date_Time_YMD";
1692 : }
1693 8 : else if (eType == OFTDate)
1694 : {
1695 4 : f.m_osDataType = "ASCII_Date_YMD";
1696 : }
1697 4 : else if (eType == OFTTime)
1698 : {
1699 4 : f.m_osDataType = "ASCII_Time";
1700 : }
1701 : else
1702 : {
1703 0 : return false;
1704 : }
1705 80 : return true;
1706 : }
1707 :
1708 : /************************************************************************/
1709 : /* ==================================================================== */
1710 : /* PDS4TableBinary */
1711 : /* ==================================================================== */
1712 : /************************************************************************/
1713 :
1714 16 : PDS4TableBinary::PDS4TableBinary(PDS4Dataset *poDS, const char *pszName,
1715 16 : const char *pszFilename, bool bUpdate)
1716 16 : : PDS4FixedWidthTable(poDS, pszName, pszFilename, bUpdate)
1717 : {
1718 16 : }
1719 :
1720 : /************************************************************************/
1721 : /* CreateFieldInternal() */
1722 : /************************************************************************/
1723 :
1724 47 : bool PDS4TableBinary::CreateFieldInternal(OGRFieldType eType,
1725 : OGRFieldSubType eSubType, int nWidth,
1726 : Field &f)
1727 : {
1728 94 : CPLString osEndianness(CPLGetConfigOption("PDS4_ENDIANNESS", "LSB"));
1729 94 : CPLString osSignedness(CPLGetConfigOption("PDS4_SIGNEDNESS", "Signed"));
1730 :
1731 47 : if (eType == OFTString)
1732 : {
1733 4 : f.m_osDataType = "UTF8_String";
1734 4 : f.m_nLength = nWidth > 0 ? nWidth : 64;
1735 : }
1736 43 : else if (eType == OFTInteger)
1737 : {
1738 28 : f.m_osDataType = nWidth > 0 && nWidth <= 2 ? osSignedness + "Byte"
1739 8 : : eSubType == OFSTBoolean ? CPLString("ASCII_Boolean")
1740 : : eSubType == OFSTInt16
1741 20 : ? osSignedness + osEndianness + "2"
1742 20 : : osSignedness + osEndianness + "4";
1743 28 : f.m_nLength = nWidth > 0 && nWidth <= 2 ? 1
1744 20 : : eSubType == OFSTBoolean ? 1
1745 8 : : eSubType == OFSTInt16 ? 2
1746 : : 4;
1747 : }
1748 27 : else if (eType == OFTInteger64)
1749 : {
1750 4 : f.m_osDataType = osSignedness + osEndianness + "8";
1751 4 : f.m_nLength = 8;
1752 : }
1753 23 : else if (eType == OFTReal)
1754 : {
1755 : f.m_osDataType = eSubType == OFSTFloat32
1756 22 : ? "IEEE754" + osEndianness + "Single"
1757 18 : : "IEEE754" + osEndianness + "Double";
1758 11 : f.m_nLength = eSubType == OFSTFloat32 ? 4 : 8;
1759 : }
1760 12 : else if (eType == OFTDateTime)
1761 : {
1762 4 : f.m_osDataType = "ASCII_Date_Time_YMD";
1763 : // YYYY-MM-DDTHH:MM:SS.sssZ
1764 4 : f.m_nLength = 4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 3 + 1;
1765 : }
1766 8 : else if (eType == OFTDate)
1767 : {
1768 4 : f.m_osDataType = "ASCII_Date_YMD";
1769 : // YYYY-MM-DD
1770 4 : f.m_nLength = 4 + 1 + 2 + 1 + 2;
1771 : }
1772 4 : else if (eType == OFTTime)
1773 : {
1774 4 : f.m_osDataType = "ASCII_Time";
1775 : // HH:MM:SS.sss
1776 4 : f.m_nLength = 2 + 1 + 2 + 1 + 2 + 1 + 3;
1777 : }
1778 : else
1779 : {
1780 0 : return false;
1781 : }
1782 47 : return true;
1783 : }
1784 :
1785 : /************************************************************************/
1786 : /* ==================================================================== */
1787 : /* PDS4DelimitedTable */
1788 : /* ==================================================================== */
1789 : /************************************************************************/
1790 :
1791 120 : PDS4DelimitedTable::PDS4DelimitedTable(PDS4Dataset *poDS, const char *pszName,
1792 120 : const char *pszFilename, bool bUpdate)
1793 120 : : PDS4TableBaseLayer(poDS, pszName, pszFilename, bUpdate)
1794 : {
1795 120 : }
1796 :
1797 : /************************************************************************/
1798 : /* ~PDS4DelimitedTable() */
1799 : /************************************************************************/
1800 :
1801 240 : PDS4DelimitedTable::~PDS4DelimitedTable()
1802 : {
1803 120 : if (m_bDirtyHeader)
1804 55 : GenerateVRT();
1805 240 : }
1806 :
1807 : /************************************************************************/
1808 : /* GenerateVRT() */
1809 : /************************************************************************/
1810 :
1811 55 : void PDS4DelimitedTable::GenerateVRT()
1812 : {
1813 55 : CPLString osVRTFilename = CPLResetExtensionSafe(m_osFilename, "vrt");
1814 55 : if (m_bCreation)
1815 : {
1816 : // In creation mode, generate the VRT, unless explicitly disabled by
1817 : // CREATE_VRT=NO
1818 55 : if (!m_aosLCO.FetchBool("CREATE_VRT", true))
1819 1 : return;
1820 : }
1821 : else
1822 : {
1823 : // In a update situation, only generates the VRT if ones already exists
1824 : VSIStatBufL sStat;
1825 0 : if (VSIStatL(osVRTFilename, &sStat) != 0)
1826 0 : return;
1827 : }
1828 :
1829 : CPLXMLNode *psRoot =
1830 54 : CPLCreateXMLNode(nullptr, CXT_Element, "OGRVRTDataSource");
1831 54 : CPLXMLNode *psLayer = CPLCreateXMLNode(psRoot, CXT_Element, "OGRVRTLayer");
1832 54 : CPLAddXMLAttributeAndValue(psLayer, "name", GetName());
1833 :
1834 54 : CPLXMLNode *psSrcDataSource = CPLCreateXMLElementAndValue(
1835 : psLayer, "SrcDataSource", CPLGetFilename(m_osFilename));
1836 54 : CPLAddXMLAttributeAndValue(psSrcDataSource, "relativeToVRT", "1");
1837 :
1838 54 : CPLCreateXMLElementAndValue(
1839 108 : psLayer, "SrcLayer", CPLGetBasenameSafe(m_osFilename.c_str()).c_str());
1840 :
1841 54 : CPLXMLNode *psLastChild = CPLCreateXMLElementAndValue(
1842 : psLayer, "GeometryType",
1843 108 : OGRVRTGetSerializedGeometryType(GetGeomType()).c_str());
1844 :
1845 54 : if (GetSpatialRef())
1846 : {
1847 1 : char *pszWKT = nullptr;
1848 1 : GetSpatialRef()->exportToWkt(&pszWKT);
1849 1 : if (pszWKT)
1850 : {
1851 1 : CPLCreateXMLElementAndValue(psLayer, "LayerSRS", pszWKT);
1852 1 : CPLFree(pszWKT);
1853 : }
1854 : }
1855 :
1856 55 : while (psLastChild->psNext)
1857 1 : psLastChild = psLastChild->psNext;
1858 54 : const int nFieldCount = m_poRawFeatureDefn->GetFieldCount();
1859 221 : for (int i = 0; i < nFieldCount; i++)
1860 : {
1861 167 : if (i != m_iWKT && i != m_iLongField && i != m_iLatField &&
1862 132 : i != m_iAltField)
1863 : {
1864 132 : OGRFieldDefn *poFieldDefn = m_poRawFeatureDefn->GetFieldDefn(i);
1865 : CPLXMLNode *psField =
1866 132 : CPLCreateXMLNode(nullptr, CXT_Element, "Field");
1867 132 : psLastChild->psNext = psField;
1868 132 : psLastChild = psField;
1869 132 : CPLAddXMLAttributeAndValue(psField, "name",
1870 : poFieldDefn->GetNameRef());
1871 132 : CPLAddXMLAttributeAndValue(
1872 : psField, "type", OGR_GetFieldTypeName(poFieldDefn->GetType()));
1873 132 : if (poFieldDefn->GetSubType() != OFSTNone)
1874 : {
1875 4 : CPLAddXMLAttributeAndValue(
1876 : psField, "subtype",
1877 : OGR_GetFieldSubTypeName(poFieldDefn->GetSubType()));
1878 : }
1879 133 : if (poFieldDefn->GetWidth() > 0 &&
1880 1 : poFieldDefn->GetType() != OFTReal)
1881 : {
1882 1 : CPLAddXMLAttributeAndValue(
1883 : psField, "width",
1884 : CPLSPrintf("%d", poFieldDefn->GetWidth()));
1885 : }
1886 132 : CPLAddXMLAttributeAndValue(psField, "src",
1887 : poFieldDefn->GetNameRef());
1888 : }
1889 : }
1890 :
1891 54 : if (m_iWKT >= 0)
1892 : {
1893 : CPLXMLNode *psField =
1894 35 : CPLCreateXMLNode(nullptr, CXT_Element, "GeometryField");
1895 35 : psLastChild->psNext = psField;
1896 35 : psLastChild = psField;
1897 35 : CPLAddXMLAttributeAndValue(psField, "encoding", "WKT");
1898 35 : CPLAddXMLAttributeAndValue(
1899 : psField, "field",
1900 35 : m_poRawFeatureDefn->GetFieldDefn(m_iWKT)->GetNameRef());
1901 : }
1902 19 : else if (m_iLongField >= 0 && m_iLatField >= 0)
1903 : {
1904 : CPLXMLNode *psField =
1905 0 : CPLCreateXMLNode(nullptr, CXT_Element, "GeometryField");
1906 0 : psLastChild->psNext = psField;
1907 0 : psLastChild = psField;
1908 0 : CPLAddXMLAttributeAndValue(psField, "encoding", "PointFromColumns");
1909 0 : CPLAddXMLAttributeAndValue(
1910 : psField, "x",
1911 0 : m_poRawFeatureDefn->GetFieldDefn(m_iLongField)->GetNameRef());
1912 0 : CPLAddXMLAttributeAndValue(
1913 : psField, "y",
1914 0 : m_poRawFeatureDefn->GetFieldDefn(m_iLatField)->GetNameRef());
1915 0 : if (m_iAltField >= 0)
1916 : {
1917 0 : CPLAddXMLAttributeAndValue(
1918 : psField, "z",
1919 0 : m_poRawFeatureDefn->GetFieldDefn(m_iAltField)->GetNameRef());
1920 : }
1921 : }
1922 :
1923 54 : CPL_IGNORE_RET_VAL(psLastChild);
1924 :
1925 54 : CPLSerializeXMLTreeToFile(psRoot, osVRTFilename);
1926 54 : CPLDestroyXMLNode(psRoot);
1927 : }
1928 :
1929 : /************************************************************************/
1930 : /* ResetReading() */
1931 : /************************************************************************/
1932 :
1933 214 : void PDS4DelimitedTable::ResetReading()
1934 : {
1935 214 : m_nFID = 1;
1936 214 : VSIFSeekL(m_fp, m_nOffset, SEEK_SET);
1937 214 : }
1938 :
1939 : /************************************************************************/
1940 : /* GetNextFeatureRaw() */
1941 : /************************************************************************/
1942 :
1943 506 : OGRFeature *PDS4DelimitedTable::GetNextFeatureRaw()
1944 : {
1945 506 : const char *pszLine = CPLReadLine2L(m_fp, 10 * 1024 * 1024, nullptr);
1946 506 : if (pszLine == nullptr)
1947 : {
1948 47 : return nullptr;
1949 : }
1950 :
1951 459 : char szDelimiter[2] = {m_chFieldDelimiter, 0};
1952 459 : char **papszFields = CSLTokenizeString2(
1953 : pszLine, szDelimiter, CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS);
1954 459 : if (CSLCount(papszFields) != m_poRawFeatureDefn->GetFieldCount())
1955 : {
1956 0 : CPLError(CE_Warning, CPLE_AppDefined,
1957 : "Did not get expected number of fields at line " CPL_FRMT_GIB,
1958 : m_nFID);
1959 : }
1960 :
1961 459 : OGRFeature *poRawFeature = new OGRFeature(m_poRawFeatureDefn);
1962 459 : poRawFeature->SetFID(m_nFID);
1963 459 : m_nFID++;
1964 4231 : for (int i = 0; i < m_poRawFeatureDefn->GetFieldCount() && papszFields &&
1965 1886 : papszFields[i];
1966 : i++)
1967 : {
1968 1886 : if (!m_aoFields[i].m_osMissingConstant.empty() &&
1969 0 : m_aoFields[i].m_osMissingConstant == papszFields[i])
1970 : {
1971 : // do nothing
1972 : }
1973 1886 : else if (m_aoFields[i].m_osDataType == "ASCII_Boolean")
1974 : {
1975 10 : poRawFeature->SetField(i, EQUAL(papszFields[i], "t") ||
1976 5 : EQUAL(papszFields[i], "1")
1977 : ? 1
1978 : : 0);
1979 : }
1980 : else
1981 : {
1982 1881 : poRawFeature->SetField(i, papszFields[i]);
1983 : }
1984 : }
1985 :
1986 459 : CSLDestroy(papszFields);
1987 :
1988 459 : OGRFeature *poFeature = AddGeometryFromFields(poRawFeature);
1989 459 : delete poRawFeature;
1990 459 : return poFeature;
1991 : }
1992 :
1993 : /************************************************************************/
1994 : /* GetNextFeature() */
1995 : /************************************************************************/
1996 :
1997 506 : OGRFeature *PDS4DelimitedTable::GetNextFeature()
1998 : {
1999 : while (true)
2000 : {
2001 506 : auto poFeature = GetNextFeatureRaw();
2002 506 : if (poFeature == nullptr)
2003 : {
2004 47 : return nullptr;
2005 : }
2006 :
2007 1028 : if ((m_poFilterGeom == nullptr ||
2008 856 : FilterGeometry(poFeature->GetGeometryRef())) &&
2009 397 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
2010 : {
2011 397 : return poFeature;
2012 : }
2013 62 : delete poFeature;
2014 62 : }
2015 : }
2016 :
2017 : /************************************************************************/
2018 : /* TestCapability() */
2019 : /************************************************************************/
2020 :
2021 358 : bool PDS4DelimitedTable::TestCapability(const char *pszCap) const
2022 : {
2023 358 : if (EQUAL(pszCap, OLCRandomRead) || EQUAL(pszCap, OLCStringsAsUTF8) ||
2024 344 : EQUAL(pszCap, OLCZGeometries))
2025 : {
2026 17 : return true;
2027 : }
2028 341 : if (EQUAL(pszCap, OLCFastFeatureCount))
2029 : {
2030 0 : return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
2031 : }
2032 341 : if (EQUAL(pszCap, OLCCreateField))
2033 : {
2034 130 : return m_poDS->GetAccess() == GA_Update && m_nFeatureCount == 0;
2035 : }
2036 211 : if (EQUAL(pszCap, OLCSequentialWrite))
2037 : {
2038 86 : return m_poDS->GetAccess() == GA_Update;
2039 : }
2040 125 : return false;
2041 : }
2042 :
2043 : /************************************************************************/
2044 : /* QuoteIfNeeded() */
2045 : /************************************************************************/
2046 :
2047 535 : CPLString PDS4DelimitedTable::QuoteIfNeeded(const char *pszVal)
2048 : {
2049 535 : if (strchr(pszVal, m_chFieldDelimiter) == nullptr)
2050 : {
2051 489 : return pszVal;
2052 : }
2053 92 : return '"' + CPLString(pszVal) + '"';
2054 : }
2055 :
2056 : /************************************************************************/
2057 : /* ICreateFeature() */
2058 : /************************************************************************/
2059 :
2060 97 : OGRErr PDS4DelimitedTable::ICreateFeature(OGRFeature *poFeature)
2061 : {
2062 97 : if (!m_bUpdate)
2063 : {
2064 0 : CPLError(CE_Failure, CPLE_NotSupported,
2065 : "Operation not supported on read-only layer");
2066 0 : return OGRERR_FAILURE;
2067 : }
2068 :
2069 97 : if (m_bAddWKTColumnPending)
2070 : {
2071 : OGRFieldDefn oFieldDefn(
2072 72 : CSLFetchNameValueDef(m_aosLCO.List(), "WKT", "WKT"), OFTString);
2073 36 : m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
2074 36 : m_iWKT = m_poRawFeatureDefn->GetFieldCount() - 1;
2075 36 : Field f;
2076 36 : f.m_osDataType = "ASCII_String";
2077 36 : m_aoFields.push_back(std::move(f));
2078 36 : m_bAddWKTColumnPending = false;
2079 : }
2080 :
2081 97 : if (m_nFeatureCount == 0)
2082 : {
2083 208 : for (int i = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
2084 : {
2085 169 : if (i > 0)
2086 : {
2087 130 : VSIFPrintfL(m_fp, "%c", m_chFieldDelimiter);
2088 : }
2089 169 : VSIFPrintfL(
2090 : m_fp, "%s",
2091 338 : QuoteIfNeeded(m_poRawFeatureDefn->GetFieldDefn(i)->GetNameRef())
2092 : .c_str());
2093 : }
2094 39 : VSIFPrintfL(m_fp, "%s", m_osLineEnding.c_str());
2095 39 : m_nOffset = VSIFTellL(m_fp);
2096 : }
2097 :
2098 97 : OGRFeature *poRawFeature = AddFieldsFromGeometry(poFeature);
2099 591 : for (int i = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
2100 : {
2101 494 : if (i > 0)
2102 : {
2103 397 : VSIFPrintfL(m_fp, "%c", m_chFieldDelimiter);
2104 : }
2105 494 : if (!poRawFeature->IsFieldSetAndNotNull(i))
2106 : {
2107 128 : if (!m_aoFields[i].m_osMissingConstant.empty())
2108 : {
2109 0 : VSIFPrintfL(
2110 : m_fp, "%s",
2111 0 : QuoteIfNeeded(m_aoFields[i].m_osMissingConstant).c_str());
2112 : }
2113 128 : continue;
2114 : }
2115 366 : VSIFPrintfL(m_fp, "%s",
2116 732 : QuoteIfNeeded(poRawFeature->GetFieldAsString(i)).c_str());
2117 : }
2118 97 : VSIFPrintfL(m_fp, "%s", m_osLineEnding.c_str());
2119 97 : delete poRawFeature;
2120 :
2121 97 : m_nFeatureCount++;
2122 97 : poFeature->SetFID(m_nFeatureCount);
2123 :
2124 97 : return OGRERR_NONE;
2125 : }
2126 :
2127 : /************************************************************************/
2128 : /* CreateField() */
2129 : /************************************************************************/
2130 :
2131 133 : OGRErr PDS4DelimitedTable::CreateField(const OGRFieldDefn *poFieldIn, int)
2132 :
2133 : {
2134 133 : if (m_poDS->GetAccess() != GA_Update)
2135 : {
2136 0 : CPLError(CE_Failure, CPLE_AppDefined,
2137 : "Dataset opened in read-only mode");
2138 0 : return OGRERR_FAILURE;
2139 : }
2140 133 : if (m_nFeatureCount > 0)
2141 : {
2142 0 : return OGRERR_FAILURE;
2143 : }
2144 :
2145 133 : const auto eType = poFieldIn->GetType();
2146 266 : Field f;
2147 133 : if (eType == OFTString)
2148 : {
2149 37 : f.m_osDataType = "UTF8_String";
2150 : }
2151 96 : else if (eType == OFTInteger)
2152 : {
2153 26 : f.m_osDataType = poFieldIn->GetSubType() == OFSTBoolean
2154 : ? "ASCII_Boolean"
2155 26 : : "ASCII_Integer";
2156 : }
2157 70 : else if (eType == OFTInteger64)
2158 : {
2159 5 : f.m_osDataType = "ASCII_Integer";
2160 : }
2161 65 : else if (eType == OFTReal)
2162 : {
2163 21 : f.m_osDataType = "ASCII_Real";
2164 : }
2165 44 : else if (eType == OFTDateTime)
2166 : {
2167 20 : f.m_osDataType = "ASCII_Date_Time_YMD";
2168 : }
2169 24 : else if (eType == OFTDate)
2170 : {
2171 20 : f.m_osDataType = "ASCII_Date_YMD";
2172 : }
2173 4 : else if (eType == OFTTime)
2174 : {
2175 4 : f.m_osDataType = "ASCII_Time";
2176 : }
2177 : else
2178 : {
2179 0 : return OGRERR_FAILURE;
2180 : }
2181 :
2182 133 : MarkHeaderDirty();
2183 133 : m_aoFields.push_back(std::move(f));
2184 133 : m_poRawFeatureDefn->AddFieldDefn(poFieldIn);
2185 133 : m_poFeatureDefn->AddFieldDefn(poFieldIn);
2186 :
2187 133 : return OGRERR_NONE;
2188 : }
2189 :
2190 : /************************************************************************/
2191 : /* ReadTableDef() */
2192 : /************************************************************************/
2193 :
2194 64 : bool PDS4DelimitedTable::ReadTableDef(const CPLXMLNode *psTable)
2195 : {
2196 64 : CPLAssert(m_fp == nullptr);
2197 64 : m_fp = VSIFOpenL(m_osFilename,
2198 64 : (m_poDS->GetAccess() == GA_ReadOnly) ? "rb" : "r+b");
2199 64 : if (!m_fp)
2200 : {
2201 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2202 : m_osFilename.c_str());
2203 0 : return false;
2204 : }
2205 :
2206 64 : m_nOffset = static_cast<GUIntBig>(
2207 64 : CPLAtoGIntBig(CPLGetXMLValue(psTable, "offset", "0")));
2208 :
2209 64 : m_nFeatureCount = CPLAtoGIntBig(CPLGetXMLValue(psTable, "records", "-1"));
2210 :
2211 : const char *pszRecordDelimiter =
2212 64 : CPLGetXMLValue(psTable, "record_delimiter", "");
2213 64 : if (EQUAL(pszRecordDelimiter, "Carriage-Return Line-Feed"))
2214 62 : m_osLineEnding = "\r\n";
2215 2 : else if (EQUAL(pszRecordDelimiter, "Line-Feed"))
2216 2 : m_osLineEnding = "\n";
2217 0 : else if (EQUAL(pszRecordDelimiter, ""))
2218 : {
2219 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing record_delimiter");
2220 0 : return false;
2221 : }
2222 : else
2223 : {
2224 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid record_delimiter");
2225 0 : return false;
2226 : }
2227 :
2228 : const char *pszFieldDelimiter =
2229 64 : CPLGetXMLValue(psTable, "field_delimiter", nullptr);
2230 64 : if (pszFieldDelimiter == nullptr)
2231 : {
2232 0 : return false;
2233 : }
2234 64 : if (EQUAL(pszFieldDelimiter, "Comma"))
2235 : {
2236 64 : m_chFieldDelimiter = ',';
2237 : }
2238 0 : else if (EQUAL(pszFieldDelimiter, "Horizontal Tab"))
2239 : {
2240 0 : m_chFieldDelimiter = '\t';
2241 : }
2242 0 : else if (EQUAL(pszFieldDelimiter, "Semicolon"))
2243 : {
2244 0 : m_chFieldDelimiter = ';';
2245 : }
2246 0 : else if (EQUAL(pszFieldDelimiter, "Vertical Bar"))
2247 : {
2248 0 : m_chFieldDelimiter = '|';
2249 : }
2250 : else
2251 : {
2252 0 : CPLError(CE_Failure, CPLE_NotSupported,
2253 : "field_delimiter value not supported");
2254 0 : return false;
2255 : }
2256 :
2257 64 : const CPLXMLNode *psRecord = CPLGetXMLNode(psTable, "Record_Delimited");
2258 64 : if (!psRecord)
2259 : {
2260 0 : return false;
2261 : }
2262 64 : if (!ReadFields(psRecord, ""))
2263 : {
2264 0 : return false;
2265 : }
2266 :
2267 64 : SetupGeomField();
2268 64 : ResetReading();
2269 :
2270 64 : return true;
2271 : }
2272 :
2273 : /************************************************************************/
2274 : /* ReadFields() */
2275 : /************************************************************************/
2276 :
2277 66 : bool PDS4DelimitedTable::ReadFields(const CPLXMLNode *psParent,
2278 : const CPLString &osSuffixFieldName)
2279 : {
2280 437 : for (const CPLXMLNode *psIter = psParent->psChild; psIter;
2281 371 : psIter = psIter->psNext)
2282 : {
2283 371 : if (psIter->eType == CXT_Element &&
2284 371 : strcmp(psIter->pszValue, "Field_Delimited") == 0)
2285 : {
2286 234 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
2287 234 : if (!pszName)
2288 : {
2289 0 : return false;
2290 : }
2291 : const char *pszDataType =
2292 234 : CPLGetXMLValue(psIter, "data_type", nullptr);
2293 234 : if (!pszDataType)
2294 : {
2295 0 : return false;
2296 : }
2297 : int nMaximumFieldLength =
2298 234 : atoi(CPLGetXMLValue(psIter, "maximum_field_length", "0"));
2299 :
2300 234 : Field f;
2301 234 : f.m_osDataType = pszDataType;
2302 234 : f.m_osUnit = CPLGetXMLValue(psIter, "unit", "");
2303 234 : f.m_osDescription = CPLGetXMLValue(psIter, "description", "");
2304 :
2305 : CPLXMLNode *psSpecialConstants = const_cast<CPLXMLNode *>(
2306 234 : CPLGetXMLNode(psIter, "Special_Constants"));
2307 234 : if (psSpecialConstants)
2308 : {
2309 0 : auto psNext = psSpecialConstants->psNext;
2310 0 : psSpecialConstants->psNext = nullptr;
2311 0 : char *pszXML = CPLSerializeXMLTree(psSpecialConstants);
2312 0 : psSpecialConstants->psNext = psNext;
2313 0 : if (pszXML)
2314 : {
2315 0 : f.m_osSpecialConstantsXML = pszXML;
2316 0 : CPLFree(pszXML);
2317 : }
2318 : }
2319 : f.m_osMissingConstant = CPLGetXMLValue(
2320 234 : psIter, "Special_Constants.missing_constant", "");
2321 :
2322 234 : m_aoFields.push_back(f);
2323 :
2324 234 : OGRFieldSubType eSubType = OFSTNone;
2325 234 : bool error = false;
2326 : auto eType =
2327 234 : GetFieldTypeFromPDS4DataType(pszDataType, 0, eSubType, error);
2328 234 : if (error)
2329 : {
2330 0 : CPLError(CE_Failure, CPLE_AppDefined,
2331 : "Binary fields not allowed");
2332 0 : return false;
2333 : }
2334 422 : if (STARTS_WITH(f.m_osDataType, "ASCII_") && eType == OFTInteger &&
2335 424 : eSubType == OFSTNone &&
2336 2 : (nMaximumFieldLength == 0 || nMaximumFieldLength >= 10))
2337 : {
2338 42 : eType = OFTInteger64;
2339 : }
2340 234 : OGRFieldDefn oFieldDefn((pszName + osSuffixFieldName).c_str(),
2341 468 : eType);
2342 234 : oFieldDefn.SetSubType(eSubType);
2343 280 : if (eType != OFTReal && (STARTS_WITH(f.m_osDataType, "ASCII_") ||
2344 46 : STARTS_WITH(f.m_osDataType, "UTF_8")))
2345 : {
2346 159 : oFieldDefn.SetWidth(nMaximumFieldLength);
2347 : }
2348 468 : m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
2349 : }
2350 137 : else if (psIter->eType == CXT_Element &&
2351 137 : strcmp(psIter->pszValue, "Group_Field_Delimited") == 0)
2352 : {
2353 : const char *pszRepetitions =
2354 1 : CPLGetXMLValue(psIter, "repetitions", nullptr);
2355 1 : if (!pszRepetitions)
2356 : {
2357 0 : return false;
2358 : }
2359 1 : int nRepetitions = std::min(1000, atoi(pszRepetitions));
2360 1 : if (nRepetitions <= 0)
2361 : {
2362 0 : return false;
2363 : }
2364 3 : for (int i = 0; i < nRepetitions; i++)
2365 : {
2366 2 : if (!ReadFields(psIter, osSuffixFieldName + "_" +
2367 : CPLSPrintf("%d", i + 1)))
2368 : {
2369 0 : return false;
2370 : }
2371 : }
2372 : }
2373 : }
2374 66 : return true;
2375 : }
2376 :
2377 : /************************************************************************/
2378 : /* RefreshFileAreaObservational() */
2379 : /************************************************************************/
2380 :
2381 55 : void PDS4DelimitedTable::RefreshFileAreaObservational(CPLXMLNode *psFAO)
2382 : {
2383 110 : CPLString osPrefix;
2384 55 : if (STARTS_WITH(psFAO->pszValue, "pds:"))
2385 0 : osPrefix = "pds:";
2386 :
2387 110 : CPLString osDescription;
2388 55 : CPLXMLNode *psTable = RefreshFileAreaObservationalBeginningCommon(
2389 : psFAO, osPrefix, "Table_Delimited", osDescription);
2390 :
2391 55 : CPLCreateXMLElementAndValue(
2392 110 : psTable, (osPrefix + "parsing_standard_id").c_str(), "PDS DSV 1");
2393 :
2394 55 : CPLCreateXMLElementAndValue(psTable, (osPrefix + "records").c_str(),
2395 : CPLSPrintf(CPL_FRMT_GIB, m_nFeatureCount));
2396 55 : if (!osDescription.empty())
2397 0 : CPLCreateXMLElementAndValue(psTable, (osPrefix + "description").c_str(),
2398 : osDescription);
2399 :
2400 55 : if (m_osLineEnding == "\r\n")
2401 : {
2402 54 : CPLCreateXMLElementAndValue(psTable,
2403 108 : (osPrefix + "record_delimiter").c_str(),
2404 : "Carriage-Return Line-Feed");
2405 : }
2406 1 : else if (m_osLineEnding == "\n")
2407 : {
2408 1 : CPLCreateXMLElementAndValue(
2409 2 : psTable, (osPrefix + "record_delimiter").c_str(), "Line-Feed");
2410 : }
2411 :
2412 55 : CPLCreateXMLElementAndValue(psTable, (osPrefix + "field_delimiter").c_str(),
2413 55 : m_chFieldDelimiter == '\t' ? "Horizontal Tab"
2414 110 : : m_chFieldDelimiter == ';' ? "Semicolon"
2415 55 : : m_chFieldDelimiter == '|' ? "Vertical Bar"
2416 : : "Comma");
2417 :
2418 : // Write Record_Delimited
2419 55 : CPLXMLNode *psRecord = CPLCreateXMLNode(
2420 110 : psTable, CXT_Element, (osPrefix + "Record_Delimited").c_str());
2421 :
2422 110 : CPLCreateXMLElementAndValue(
2423 110 : psRecord, (osPrefix + "fields").c_str(),
2424 55 : CPLSPrintf("%d", static_cast<int>(m_aoFields.size())));
2425 :
2426 55 : CPLXMLNode *psLastChild = CPLCreateXMLElementAndValue(
2427 110 : psRecord, (osPrefix + "groups").c_str(), "0");
2428 :
2429 55 : CPLAssert(static_cast<int>(m_aoFields.size()) ==
2430 : m_poRawFeatureDefn->GetFieldCount());
2431 :
2432 110 : const auto osPrefixedFieldDelimited(osPrefix + "Field_Delimited");
2433 110 : const auto osPrefixedName(osPrefix + "name");
2434 110 : const auto osPrefixedFieldNumber(osPrefix + "field_number");
2435 110 : const auto osPrefixedFieldData(osPrefix + "data_type");
2436 110 : const auto osPrefixMaxFieldLength(osPrefix + "maximum_field_length");
2437 110 : const auto osPrefixedUnit(osPrefix + "unit");
2438 110 : const auto osPrefixedDescription(osPrefix + "description");
2439 55 : CPLAssert(psLastChild->psNext == nullptr);
2440 224 : for (int i = 0; i < static_cast<int>(m_aoFields.size()); i++)
2441 : {
2442 169 : const auto &f = m_aoFields[i];
2443 :
2444 169 : CPLXMLNode *psField = CPLCreateXMLNode(
2445 : nullptr, CXT_Element, osPrefixedFieldDelimited.c_str());
2446 169 : psLastChild->psNext = psField;
2447 169 : psLastChild = psField;
2448 :
2449 169 : CPLCreateXMLElementAndValue(
2450 : psField, osPrefixedName.c_str(),
2451 169 : m_poRawFeatureDefn->GetFieldDefn(i)->GetNameRef());
2452 :
2453 169 : CPLCreateXMLElementAndValue(psField, osPrefixedFieldNumber.c_str(),
2454 : CPLSPrintf("%d", i + 1));
2455 :
2456 169 : CPLCreateXMLElementAndValue(psField, osPrefixedFieldData.c_str(),
2457 : f.m_osDataType.c_str());
2458 :
2459 169 : int nWidth = m_poRawFeatureDefn->GetFieldDefn(i)->GetWidth();
2460 169 : if (nWidth > 0)
2461 : {
2462 1 : auto psfield_length = CPLCreateXMLElementAndValue(
2463 : psField, osPrefixMaxFieldLength.c_str(),
2464 : CPLSPrintf("%d", nWidth));
2465 1 : CPLAddXMLAttributeAndValue(psfield_length, "unit", "byte");
2466 : }
2467 :
2468 169 : if (!f.m_osUnit.empty())
2469 : {
2470 0 : CPLCreateXMLElementAndValue(psField, osPrefixedUnit.c_str(),
2471 0 : m_aoFields[i].m_osUnit.c_str());
2472 : }
2473 :
2474 169 : if (!f.m_osDescription.empty())
2475 : {
2476 0 : CPLCreateXMLElementAndValue(psField, osPrefixedDescription.c_str(),
2477 0 : m_aoFields[i].m_osDescription.c_str());
2478 : }
2479 :
2480 169 : if (!f.m_osSpecialConstantsXML.empty())
2481 : {
2482 : auto psSpecialConstants =
2483 0 : CPLParseXMLString(f.m_osSpecialConstantsXML);
2484 0 : if (psSpecialConstants)
2485 : {
2486 0 : CPLAddXMLChild(psField, psSpecialConstants);
2487 : }
2488 : }
2489 : }
2490 55 : }
2491 :
2492 : /************************************************************************/
2493 : /* GetFileList() */
2494 : /************************************************************************/
2495 :
2496 53 : char **PDS4DelimitedTable::GetFileList() const
2497 : {
2498 53 : auto papszFileList = PDS4TableBaseLayer::GetFileList();
2499 53 : CPLString osVRTFilename = CPLResetExtensionSafe(m_osFilename, "vrt");
2500 : VSIStatBufL sStat;
2501 53 : if (VSIStatL(osVRTFilename, &sStat) == 0)
2502 : {
2503 52 : papszFileList = CSLAddString(papszFileList, osVRTFilename);
2504 : }
2505 106 : return papszFileList;
2506 : }
2507 :
2508 : /************************************************************************/
2509 : /* InitializeNewLayer() */
2510 : /************************************************************************/
2511 :
2512 56 : bool PDS4DelimitedTable::InitializeNewLayer(const OGRSpatialReference *poSRS,
2513 : bool bForceGeographic,
2514 : OGRwkbGeometryType eGType,
2515 : const char *const *papszOptions)
2516 : {
2517 56 : CPLAssert(m_fp == nullptr);
2518 56 : m_fp = VSIFOpenL(m_osFilename, "wb+");
2519 56 : if (!m_fp)
2520 : {
2521 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s",
2522 : m_osFilename.c_str());
2523 1 : return false;
2524 : }
2525 55 : m_aosLCO.Assign(CSLDuplicate(papszOptions));
2526 55 : m_bCreation = true;
2527 :
2528 : // For testing purposes
2529 55 : m_chFieldDelimiter = CPLGetConfigOption("OGR_PDS4_FIELD_DELIMITER", ",")[0];
2530 :
2531 : const char *pszGeomColumns =
2532 55 : CSLFetchNameValueDef(papszOptions, "GEOM_COLUMNS", "AUTO");
2533 55 : if ((EQUAL(pszGeomColumns, "AUTO") && wkbFlatten(eGType) == wkbPoint &&
2534 116 : (bForceGeographic || (poSRS && poSRS->IsGeographic()))) ||
2535 55 : (EQUAL(pszGeomColumns, "LONG_LAT") && eGType != wkbNone))
2536 : {
2537 : {
2538 : OGRFieldDefn oFieldDefn(
2539 0 : CSLFetchNameValueDef(papszOptions, "LAT", "Latitude"), OFTReal);
2540 0 : m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
2541 0 : m_iLatField = m_poRawFeatureDefn->GetFieldCount() - 1;
2542 0 : Field f;
2543 0 : f.m_osDataType = "ASCII_Real";
2544 0 : m_aoFields.push_back(std::move(f));
2545 : }
2546 : {
2547 : OGRFieldDefn oFieldDefn(
2548 : CSLFetchNameValueDef(papszOptions, "LONG", "Longitude"),
2549 0 : OFTReal);
2550 0 : m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
2551 0 : m_iLongField = m_poRawFeatureDefn->GetFieldCount() - 1;
2552 0 : Field f;
2553 0 : f.m_osDataType = "ASCII_Real";
2554 0 : m_aoFields.push_back(std::move(f));
2555 : }
2556 0 : if (eGType == wkbPoint25D)
2557 : {
2558 : OGRFieldDefn oFieldDefn(
2559 0 : CSLFetchNameValueDef(papszOptions, "ALT", "Altitude"), OFTReal);
2560 0 : m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
2561 0 : m_iAltField = m_poRawFeatureDefn->GetFieldCount() - 1;
2562 0 : Field f;
2563 0 : f.m_osDataType = "ASCII_Real";
2564 0 : m_aoFields.push_back(std::move(f));
2565 : }
2566 : }
2567 55 : else if (eGType != wkbNone &&
2568 51 : (EQUAL(pszGeomColumns, "AUTO") || EQUAL(pszGeomColumns, "WKT")))
2569 : {
2570 51 : m_bAddWKTColumnPending = true;
2571 : }
2572 :
2573 55 : if (eGType != wkbNone)
2574 : {
2575 51 : m_poRawFeatureDefn->SetGeomType(eGType);
2576 :
2577 51 : m_poFeatureDefn->SetGeomType(eGType);
2578 51 : if (poSRS)
2579 : {
2580 2 : auto poSRSClone = poSRS->Clone();
2581 2 : poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2582 2 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRSClone);
2583 2 : poSRSClone->Release();
2584 : }
2585 : }
2586 :
2587 55 : ParseLineEndingOption(papszOptions);
2588 :
2589 55 : m_nFeatureCount = 0;
2590 55 : MarkHeaderDirty();
2591 55 : return true;
2592 : }
2593 :
2594 : /************************************************************************/
2595 : /* ==================================================================== */
2596 : /* PDS4EditableSynchronizer */
2597 : /* ==================================================================== */
2598 : /************************************************************************/
2599 :
2600 : template <class T>
2601 : class PDS4EditableSynchronizer final : public IOGREditableLayerSynchronizer
2602 : {
2603 : public:
2604 74 : PDS4EditableSynchronizer() = default;
2605 :
2606 : OGRErr EditableSyncToDisk(OGRLayer *poEditableLayer,
2607 : OGRLayer **ppoDecoratedLayer) override;
2608 : };
2609 :
2610 : template <class T>
2611 : OGRErr
2612 3 : PDS4EditableSynchronizer<T>::EditableSyncToDisk(OGRLayer *poEditableLayer,
2613 : OGRLayer **ppoDecoratedLayer)
2614 : {
2615 3 : auto poOriLayer = cpl::down_cast<T *>(*ppoDecoratedLayer);
2616 :
2617 6 : CPLString osTmpFilename(poOriLayer->m_osFilename + ".tmp");
2618 3 : auto poNewLayer = poOriLayer->NewLayer(
2619 : poOriLayer->m_poDS, poOriLayer->GetName(), osTmpFilename);
2620 6 : CPLStringList aosLCO(poOriLayer->m_aosLCO);
2621 3 : if (poOriLayer->m_iLatField >= 0)
2622 : {
2623 2 : aosLCO.SetNameValue("LAT", poOriLayer->m_poRawFeatureDefn
2624 : ->GetFieldDefn(poOriLayer->m_iLatField)
2625 : ->GetNameRef());
2626 : }
2627 3 : if (poOriLayer->m_iLongField >= 0)
2628 : {
2629 2 : aosLCO.SetNameValue("LONG", poOriLayer->m_poRawFeatureDefn
2630 : ->GetFieldDefn(poOriLayer->m_iLongField)
2631 : ->GetNameRef());
2632 : }
2633 3 : if (poOriLayer->m_iAltField >= 0)
2634 : {
2635 2 : aosLCO.SetNameValue("ALT", poOriLayer->m_poRawFeatureDefn
2636 : ->GetFieldDefn(poOriLayer->m_iAltField)
2637 : ->GetNameRef());
2638 : }
2639 6 : if (!poNewLayer->InitializeNewLayer(
2640 3 : poOriLayer->GetSpatialRef(), poOriLayer->m_iLatField >= 0,
2641 3 : poOriLayer->GetGeomType(), aosLCO.List()))
2642 : {
2643 0 : delete poNewLayer;
2644 0 : VSIUnlink(osTmpFilename);
2645 0 : return OGRERR_FAILURE;
2646 : }
2647 :
2648 : const auto copyField =
2649 94 : [](typename T::Field &oDst, const typename T::Field &oSrc)
2650 : {
2651 47 : oDst.m_osDescription = oSrc.m_osDescription;
2652 47 : oDst.m_osUnit = oSrc.m_osUnit;
2653 47 : oDst.m_osSpecialConstantsXML = oSrc.m_osSpecialConstantsXML;
2654 : };
2655 :
2656 3 : if (poNewLayer->m_iLatField >= 0)
2657 : {
2658 2 : copyField(poNewLayer->m_aoFields[poNewLayer->m_iLatField],
2659 2 : poOriLayer->m_aoFields[poOriLayer->m_iLatField]);
2660 : }
2661 3 : if (poNewLayer->m_iLongField >= 0)
2662 : {
2663 2 : copyField(poNewLayer->m_aoFields[poNewLayer->m_iLongField],
2664 2 : poOriLayer->m_aoFields[poOriLayer->m_iLongField]);
2665 : }
2666 3 : if (poNewLayer->m_iAltField >= 0)
2667 : {
2668 2 : copyField(poNewLayer->m_aoFields[poNewLayer->m_iAltField],
2669 2 : poOriLayer->m_aoFields[poOriLayer->m_iAltField]);
2670 : }
2671 :
2672 3 : OGRFeatureDefn *poEditableFDefn = poEditableLayer->GetLayerDefn();
2673 44 : for (int i = 0; i < poEditableFDefn->GetFieldCount(); i++)
2674 : {
2675 41 : auto poFieldDefn = poEditableFDefn->GetFieldDefn(i);
2676 41 : poNewLayer->CreateField(poFieldDefn, false);
2677 41 : int idx = poOriLayer->m_poRawFeatureDefn->GetFieldIndex(
2678 : poFieldDefn->GetNameRef());
2679 41 : if (idx >= 0)
2680 : {
2681 41 : copyField(poNewLayer->m_aoFields.back(),
2682 41 : poOriLayer->m_aoFields[idx]);
2683 82 : OGRFieldDefn *poOriFieldDefn =
2684 41 : poOriLayer->m_poRawFeatureDefn->GetFieldDefn(idx);
2685 41 : if (poFieldDefn->GetType() == poOriFieldDefn->GetType())
2686 : {
2687 41 : poNewLayer->m_aoFields.back().m_osDataType =
2688 41 : poOriLayer->m_aoFields[idx].m_osDataType;
2689 : }
2690 : }
2691 : }
2692 :
2693 3 : poEditableLayer->ResetReading();
2694 :
2695 : // Disable all filters.
2696 3 : const char *pszQueryStringConst = poEditableLayer->GetAttrQueryString();
2697 3 : char *pszQueryStringBak =
2698 2 : pszQueryStringConst ? CPLStrdup(pszQueryStringConst) : nullptr;
2699 3 : poEditableLayer->SetAttributeFilter(nullptr);
2700 :
2701 3 : const int iFilterGeomIndexBak = poEditableLayer->GetGeomFieldFilter();
2702 3 : OGRGeometry *poFilterGeomBak = poEditableLayer->GetSpatialFilter();
2703 3 : if (poFilterGeomBak)
2704 0 : poFilterGeomBak = poFilterGeomBak->clone();
2705 3 : poEditableLayer->SetSpatialFilter(nullptr);
2706 :
2707 6 : auto aoMapSrcToTargetIdx = poNewLayer->GetLayerDefn()->ComputeMapForSetFrom(
2708 3 : poEditableLayer->GetLayerDefn(), true);
2709 3 : aoMapSrcToTargetIdx.push_back(
2710 3 : -1); // add dummy entry to be sure that .data() is valid
2711 :
2712 3 : OGRErr eErr = OGRERR_NONE;
2713 45 : for (auto &&poFeature : poEditableLayer)
2714 : {
2715 21 : OGRFeature *poNewFeature = new OGRFeature(poNewLayer->GetLayerDefn());
2716 21 : poNewFeature->SetFrom(poFeature.get(), aoMapSrcToTargetIdx.data(),
2717 : true);
2718 21 : eErr = poNewLayer->CreateFeature(poNewFeature);
2719 21 : delete poNewFeature;
2720 21 : if (eErr != OGRERR_NONE)
2721 : {
2722 0 : break;
2723 : }
2724 : }
2725 :
2726 : // Restore filters.
2727 3 : poEditableLayer->SetAttributeFilter(pszQueryStringBak);
2728 3 : CPLFree(pszQueryStringBak);
2729 3 : poEditableLayer->SetSpatialFilter(iFilterGeomIndexBak, poFilterGeomBak);
2730 3 : delete poFilterGeomBak;
2731 :
2732 6 : if (eErr != OGRERR_NONE ||
2733 3 : !poNewLayer->RenameFileTo(poOriLayer->GetFileName()))
2734 : {
2735 0 : delete poNewLayer;
2736 0 : VSIUnlink(osTmpFilename);
2737 0 : return OGRERR_FAILURE;
2738 : }
2739 :
2740 3 : delete poOriLayer;
2741 3 : *ppoDecoratedLayer = poNewLayer;
2742 :
2743 3 : return OGRERR_NONE;
2744 : }
2745 :
2746 : /************************************************************************/
2747 : /* ==================================================================== */
2748 : /* PDS4EditableLayer */
2749 : /* ==================================================================== */
2750 : /************************************************************************/
2751 :
2752 18 : PDS4EditableLayer::PDS4EditableLayer(
2753 18 : std::unique_ptr<PDS4FixedWidthTable> poBaseLayer)
2754 : : OGREditableLayer(
2755 18 : poBaseLayer.release(), true,
2756 18 : std::make_unique<PDS4EditableSynchronizer<PDS4FixedWidthTable>>()
2757 18 : .release(),
2758 54 : true)
2759 : {
2760 18 : }
2761 :
2762 56 : PDS4EditableLayer::PDS4EditableLayer(
2763 56 : std::unique_ptr<PDS4DelimitedTable> poBaseLayer)
2764 : : OGREditableLayer(
2765 56 : poBaseLayer.release(), true,
2766 56 : std::make_unique<PDS4EditableSynchronizer<PDS4DelimitedTable>>()
2767 56 : .release(),
2768 168 : true)
2769 : {
2770 56 : }
2771 :
2772 : PDS4EditableLayer::~PDS4EditableLayer() = default;
2773 :
2774 : /************************************************************************/
2775 : /* GetBaseLayer() */
2776 : /************************************************************************/
2777 :
2778 505 : PDS4TableBaseLayer *PDS4EditableLayer::GetBaseLayer() const
2779 : {
2780 505 : return cpl::down_cast<PDS4TableBaseLayer *>(
2781 505 : OGREditableLayer::GetBaseLayer());
2782 : }
2783 :
2784 : /************************************************************************/
2785 : /* SetSpatialRef() */
2786 : /************************************************************************/
2787 :
2788 1 : void PDS4EditableLayer::SetSpatialRef(OGRSpatialReference *poSRS)
2789 : {
2790 1 : if (GetGeomType() != wkbNone)
2791 : {
2792 1 : GetLayerDefn()->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
2793 1 : GetBaseLayer()->GetLayerDefn()->GetGeomFieldDefn(0)->SetSpatialRef(
2794 : poSRS);
2795 : }
2796 1 : }
|