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