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