Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: CPHD driver multidimensional classes
5 : * Author: Norman Barker <norman at analyticaspatial.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2026, Norman Barker <norman at analyticaspatial.com>
9 : *
10 : ****************************************************************************/
11 :
12 : #include <cmath>
13 : #include <functional>
14 : #include <iostream>
15 : #include <limits>
16 :
17 : #include "cpl_vsi_virtual.h"
18 : #include "gdal_frmts.h"
19 : #include "gdal_multidim.h"
20 : #include "memmultidim.h"
21 : #include "rawdataset.h"
22 :
23 : static int CPHDDatasetIdentify(GDALOpenInfo *poOpenInfo);
24 :
25 : constexpr const char *PVP_ARRAY_NAME = "PVP";
26 :
27 : /************************************************************************/
28 : /* ParseComplexDataType */
29 : /************************************************************************/
30 :
31 3 : static GDALDataType ParseComplexDataType(const char *pszFormat,
32 : const char *pszFileName)
33 : {
34 3 : GDALDataType dt = GDT_Unknown;
35 3 : if EQUAL (pszFormat, "CI4")
36 1 : dt = GDT_CInt16;
37 2 : else if EQUAL (pszFormat, "CI8")
38 0 : dt = GDT_CInt32;
39 2 : else if (EQUAL(pszFormat, "CI2") || EQUAL(pszFormat, "CI16"))
40 0 : CPLError(CE_Failure, CPLE_AppDefined, "Format %s not supported : %s.",
41 : pszFormat, pszFileName);
42 2 : else if EQUAL (pszFormat, "CF8")
43 2 : dt = GDT_CFloat32;
44 0 : else if EQUAL (pszFormat, "CF16")
45 0 : dt = GDT_CFloat64;
46 : else
47 0 : CPLError(CE_Failure, CPLE_AppDefined, "Format %s not recognized : %s.",
48 : pszFormat, pszFileName);
49 :
50 3 : return dt;
51 : }
52 :
53 : /************************************************************************/
54 : /* ParsePVPDataType */
55 : /************************************************************************/
56 :
57 3 : static GDALExtendedDataType ParsePVPDataType(const CPLXMLNode *psPvpXML,
58 : const char *pszFileName)
59 : {
60 6 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
61 3 : bool bSort = false;
62 3 : size_t nSize = 0;
63 3 : int nLastOffset = 0;
64 3 : auto *psPvpChild = psPvpXML->psChild;
65 :
66 6 : std::function<bool(const CPLXMLNode *, const char *)> parseChildPVPDataType;
67 : parseChildPVPDataType =
68 70 : [&parseChildPVPDataType, &bSort, &comps, &nSize, &nLastOffset,
69 278 : &pszFileName](const CPLXMLNode *psNode, const char *pszPrefix)
70 : {
71 70 : if (psNode->eType != CXT_Element)
72 : {
73 0 : CPLError(CE_Failure, CPLE_AppDefined,
74 : "Unexpected XML error parsing : %s", pszFileName);
75 0 : return false;
76 : }
77 : auto osElementName =
78 : (pszPrefix == nullptr)
79 64 : ? std::string(psNode->pszValue)
80 164 : : std::string(pszPrefix) + std::string(psNode->pszValue);
81 70 : if ((osElementName == "TxAntenna") || (osElementName == "RcvAntenna"))
82 : {
83 2 : auto *psChild = psNode->psChild;
84 8 : for (; psChild != nullptr; psChild = psChild->psNext)
85 : {
86 6 : if (!parseChildPVPDataType(
87 12 : psChild, (std::string(psNode->pszValue) + ".").c_str()))
88 : {
89 0 : CPLError(CE_Failure, CPLE_AppDefined,
90 : "Expected child elements for %s : %s",
91 : osElementName.c_str(), pszFileName);
92 0 : return false;
93 : }
94 : }
95 2 : return true;
96 : }
97 68 : else if (osElementName == "AddedPVP")
98 : {
99 2 : osElementName = CPLGetXMLValue(psNode, "Name", "");
100 : }
101 :
102 68 : const auto pszFormat = CPLGetXMLValue(psNode, "Format", nullptr);
103 68 : const auto pszOffset = CPLGetXMLValue(psNode, "Offset", nullptr);
104 :
105 68 : if (pszFormat && pszOffset)
106 : {
107 : // all values are multiples of 8 bytes
108 68 : auto nOffset = atoi(pszOffset);
109 68 : if (nOffset < 0 || nOffset > (std::numeric_limits<int>::max() / 8))
110 : {
111 0 : CPLError(CE_Failure, CPLE_AppDefined,
112 : "Invalid offset %s for %s in %s.", pszOffset,
113 : osElementName.c_str(), pszFileName);
114 0 : return false;
115 : }
116 68 : nOffset *= 8;
117 :
118 68 : if (nOffset < nLastOffset)
119 11 : bSort = true;
120 : else
121 57 : nLastOffset = nOffset;
122 :
123 68 : if EQUAL (pszFormat, "X=F8;Y=F8;Z=F8;")
124 : {
125 38 : std::vector<std::unique_ptr<GDALEDTComponent>> xyzComps;
126 : xyzComps.emplace_back(
127 76 : std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
128 57 : "X", 0, GDALExtendedDataType::Create(GDT_Float64))));
129 : xyzComps.emplace_back(
130 76 : std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
131 57 : "Y", 8, GDALExtendedDataType::Create(GDT_Float64))));
132 : xyzComps.emplace_back(
133 76 : std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
134 57 : "Z", 16, GDALExtendedDataType::Create(GDT_Float64))));
135 : auto dtXYZ(GDALExtendedDataType::Create("XYZ", 24,
136 38 : std::move(xyzComps)));
137 38 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
138 38 : new GDALEDTComponent(osElementName, nOffset, dtXYZ)));
139 19 : nSize += 24;
140 : }
141 49 : else if EQUAL (pszFormat, "DCX=F8;DCY=F8;")
142 : {
143 4 : std::vector<std::unique_ptr<GDALEDTComponent>> xyComps;
144 : xyComps.emplace_back(
145 8 : std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
146 6 : "DCX", 0, GDALExtendedDataType::Create(GDT_Float64))));
147 : xyComps.emplace_back(
148 8 : std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
149 6 : "DCY", 8, GDALExtendedDataType::Create(GDT_Float64))));
150 : auto dtXY(GDALExtendedDataType::Create("DCXDCY", 16,
151 4 : std::move(xyComps)));
152 4 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
153 4 : new GDALEDTComponent(osElementName, nOffset, dtXY)));
154 2 : nSize += 16;
155 : }
156 47 : else if EQUAL (pszFormat, "F8")
157 : {
158 : comps.emplace_back(
159 86 : std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
160 : osElementName, nOffset,
161 129 : GDALExtendedDataType::Create(GDT_Float64))));
162 43 : nSize += 8;
163 : }
164 4 : else if EQUAL (pszFormat, "I8")
165 : {
166 : comps.emplace_back(
167 8 : std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
168 : osElementName, nOffset,
169 12 : GDALExtendedDataType::Create(GDT_Int64))));
170 4 : nSize += 8;
171 : }
172 : else
173 : {
174 0 : CPLError(CE_Failure, CPLE_AppDefined,
175 : "Unrecognized format %s for %s : %s.", pszFormat,
176 : osElementName.c_str(), pszFileName);
177 0 : return false;
178 68 : }
179 : }
180 : else
181 : {
182 0 : CPLError(CE_Failure, CPLE_AppDefined,
183 : "Expected offset or format for %s : %s",
184 : osElementName.c_str(), pszFileName);
185 0 : return false;
186 : }
187 68 : return true;
188 3 : };
189 :
190 67 : for (; psPvpChild != nullptr; psPvpChild = psPvpChild->psNext)
191 : {
192 64 : if (!parseChildPVPDataType(psPvpChild, nullptr))
193 0 : return GDALExtendedDataType::Create(GDT_Unknown);
194 : }
195 :
196 3 : if (bSort)
197 : {
198 1 : sort(comps.begin(), comps.end(),
199 135 : [](const std::unique_ptr<GDALEDTComponent> &comp1,
200 : const std::unique_ptr<GDALEDTComponent> &comp2)
201 135 : { return comp1->GetOffset() < comp2->GetOffset(); });
202 : }
203 :
204 3 : return GDALExtendedDataType::Create("PVPDataType", nSize, std::move(comps));
205 : }
206 :
207 : /************************************************************************/
208 : /* CPHDSharedResource */
209 : /************************************************************************/
210 :
211 : struct CPHDSharedResources
212 : {
213 : VSIVirtualHandleUniquePtr m_fp;
214 : std::string m_osFilename;
215 : CPLXMLTreeCloser m_poXMLTree;
216 :
217 : GIntBig nXmlBlockSize = 0;
218 : vsi_l_offset nXmlBlockByteOffset = 0;
219 : GIntBig nSupportBlockSize = 0;
220 : vsi_l_offset nSupportBlockByteOffset = 0;
221 : GIntBig nPVPBlockSize = 0;
222 : vsi_l_offset nPVPBlockByteOffset = 0;
223 : vsi_l_offset nPVPArrayByteOffset = 0;
224 : GIntBig nSignalBlockSize = 0;
225 : vsi_l_offset nSignalBlockByteOffset = 0;
226 :
227 3 : CPHDSharedResources(const std::string &osFilename, VSILFILE *fp)
228 3 : : m_fp(fp), m_osFilename(osFilename), m_poXMLTree(nullptr)
229 : {
230 3 : }
231 : };
232 :
233 : /************************************************************************/
234 : /* CPHDInternalDataset */
235 : /************************************************************************/
236 :
237 : class CPHDInternalDataset final : public RawDataset
238 : {
239 : friend class CPHDGroup;
240 :
241 : protected:
242 : CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override;
243 : };
244 :
245 : /************************************************************************/
246 : /* Close */
247 : /************************************************************************/
248 :
249 0 : CPLErr CPHDInternalDataset::Close(GDALProgressFunc, void *)
250 :
251 : {
252 0 : return CE_None;
253 : }
254 :
255 : /************************************************************************/
256 : /* CPHDInternalBand */
257 : /************************************************************************/
258 :
259 : class CPHDInternalBand final : public RawRasterBand
260 : {
261 : public:
262 5 : CPHDInternalBand(CPHDInternalDataset *poDSIn, int nBandIn, VSILFILE *fpRaw,
263 : vsi_l_offset nImgOffsetIn, int nPixelOffsetIn,
264 : int nLineOffsetIn, GDALDataType eDataTypeIn)
265 5 : : RawRasterBand(poDSIn, nBandIn, fpRaw, nImgOffsetIn, nPixelOffsetIn,
266 : nLineOffsetIn, eDataTypeIn,
267 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
268 5 : RawRasterBand::OwnFP::NO)
269 : {
270 5 : }
271 :
272 : ~CPHDInternalBand() override;
273 : };
274 :
275 : /************************************************************************/
276 : /* ~CPHDInternalBand */
277 : /************************************************************************/
278 :
279 10 : CPHDInternalBand::~CPHDInternalBand()
280 :
281 : {
282 10 : }
283 :
284 : /************************************************************************/
285 : /* CPHDMDArray */
286 : /************************************************************************/
287 :
288 : class CPHDMDArray final : public GDALMDArray
289 : {
290 : CPL_DISALLOW_COPY_ASSIGN(CPHDMDArray)
291 :
292 : friend class CPHDGroup;
293 :
294 : std::unique_ptr<CPHDInternalDataset> m_poDS;
295 : GDALExtendedDataType m_dt;
296 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
297 : mutable std::vector<std::shared_ptr<GDALAttribute>> m_apoAttributes{};
298 : std::string m_osFilename{};
299 :
300 : protected:
301 5 : CPHDMDArray(std::unique_ptr<CPHDInternalDataset> poDS,
302 : const std::string &osParentName, const std::string &osName,
303 : const GDALExtendedDataType &oEDT)
304 5 : : GDALAbstractMDArray(osParentName, osName),
305 5 : GDALMDArray(osParentName, osName), m_poDS(std::move(poDS)),
306 5 : m_dt(oEDT), m_osFilename(m_poDS->GetDescription())
307 : {
308 5 : const int nXSize = m_poDS->GetRasterXSize();
309 5 : const int nYSize = m_poDS->GetRasterYSize();
310 :
311 5 : std::string osArrayPath = osParentName.empty()
312 : ? "/" + osName
313 8 : : "/" + osParentName + "/" + osName;
314 :
315 30 : m_dims = {std::make_shared<GDALDimensionWeakIndexingVar>(
316 : osArrayPath, "Y", "", "", nYSize),
317 10 : std::make_shared<GDALDimensionWeakIndexingVar>(
318 15 : osArrayPath, "X", "", "", nXSize)};
319 5 : }
320 :
321 3 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
322 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
323 : const GDALExtendedDataType &bufferDataType,
324 : void *pDstBuffer) const override
325 : {
326 3 : const size_t iDimX = 1;
327 3 : const size_t iDimY = 0;
328 3 : return GDALMDRasterIOFromBand(m_poDS->GetRasterBand(1), GF_Read, iDimX,
329 : iDimY, arrayStartIdx, count, arrayStep,
330 3 : bufferStride, bufferDataType, pDstBuffer);
331 : }
332 :
333 : public:
334 : static std::shared_ptr<CPHDMDArray>
335 5 : Create(std::unique_ptr<CPHDInternalDataset> poDS,
336 : const std::string &osParentName, const std::string &osName,
337 : const GDALExtendedDataType &oEDT)
338 : {
339 : auto array(std::shared_ptr<CPHDMDArray>(
340 5 : new CPHDMDArray(std::move(poDS), osParentName, osName, oEDT)));
341 5 : array->SetSelf(array);
342 5 : return array;
343 : }
344 :
345 0 : bool IsWritable() const override
346 : {
347 0 : return false;
348 : }
349 :
350 3 : const std::string &GetFilename() const override
351 : {
352 3 : return m_osFilename;
353 : }
354 :
355 : const std::vector<std::shared_ptr<GDALDimension>> &
356 24 : GetDimensions() const override
357 : {
358 24 : return m_dims;
359 : }
360 :
361 6 : const GDALExtendedDataType &GetDataType() const override
362 : {
363 6 : return m_dt;
364 : }
365 :
366 0 : std::vector<GUInt64> GetBlockSize() const override
367 : {
368 0 : int nBlockXSize = 0;
369 0 : int nBlockYSize = 0;
370 0 : m_poDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
371 0 : return std::vector<GUInt64>{static_cast<GUInt64>(nBlockYSize),
372 0 : static_cast<GUInt64>(nBlockXSize)};
373 : }
374 :
375 : std::vector<std::shared_ptr<GDALAttribute>>
376 0 : GetAttributes(CSLConstList) const override
377 : {
378 0 : return m_apoAttributes;
379 : }
380 :
381 : ~CPHDMDArray() override;
382 : };
383 :
384 : /************************************************************************/
385 : /* ~CPHDMDArray */
386 : /************************************************************************/
387 :
388 10 : CPHDMDArray::~CPHDMDArray()
389 :
390 : {
391 10 : }
392 :
393 : /************************************************************************/
394 : /* CPHDGroup */
395 : /************************************************************************/
396 :
397 : class CPHDGroup final : public GDALGroup
398 : {
399 : friend class CPHDDataset;
400 : std::shared_ptr<CPHDSharedResources> m_poShared{};
401 : mutable std::vector<std::shared_ptr<GDALMDArray>> m_apoArrays{};
402 : mutable std::vector<std::shared_ptr<CPHDGroup>> m_apoGroups{};
403 : mutable std::vector<std::shared_ptr<GDALAttribute>> m_apoAttributes{};
404 : mutable std::vector<GByte> m_abyPVPData{};
405 : bool Init();
406 : std::shared_ptr<CPHDGroup> AddChannel(const CPLXMLNode *psChannel);
407 : bool AddSupportArray(const CPLXMLNode *psSupportArray);
408 :
409 : public:
410 6 : CPHDGroup(const std::string &osParentName, const std::string &osName,
411 : const std::shared_ptr<CPHDSharedResources> &poShared)
412 6 : : GDALGroup(osParentName, osName), m_poShared(poShared)
413 : {
414 6 : }
415 :
416 : std::vector<std::string>
417 : GetMDArrayNames(CSLConstList papszOptions) const override;
418 : std::shared_ptr<GDALMDArray>
419 : OpenMDArray(const std::string &osName,
420 : CSLConstList papszOptions) const override;
421 :
422 : std::vector<std::string>
423 : GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const override;
424 :
425 : std::shared_ptr<GDALGroup>
426 : OpenGroup(const std::string &osName,
427 : CSLConstList papszOptions = nullptr) const override;
428 :
429 : std::vector<std::shared_ptr<GDALAttribute>>
430 25 : GetAttributes(CSLConstList) const override
431 : {
432 25 : return m_apoAttributes;
433 : }
434 : };
435 :
436 : /************************************************************************/
437 : /* ==================================================================== */
438 : /* CPHDDataset */
439 : /* ==================================================================== */
440 : /************************************************************************/
441 :
442 : class CPHDDataset final : public RawDataset
443 : {
444 : CPL_DISALLOW_COPY_ASSIGN(CPHDDataset)
445 : std::shared_ptr<GDALGroup> m_poRootGroup{};
446 :
447 : protected:
448 : CPLErr Close(GDALProgressFunc, void *) override;
449 :
450 : public:
451 3 : CPHDDataset()
452 3 : {
453 3 : }
454 :
455 : static GDALDataset *OpenMultiDim(GDALOpenInfo *poOpenInfo);
456 :
457 3 : std::shared_ptr<GDALGroup> GetRootGroup() const override
458 : {
459 3 : return m_poRootGroup;
460 : }
461 :
462 : static GDALDataset *Open(GDALOpenInfo *);
463 : };
464 :
465 : /************************************************************************/
466 : /* Close */
467 : /************************************************************************/
468 :
469 3 : CPLErr CPHDDataset::Close(GDALProgressFunc, void *)
470 :
471 : {
472 3 : return CE_None;
473 : }
474 :
475 : /************************************************************************/
476 : /* OpenMultiDim() */
477 : /************************************************************************/
478 :
479 3 : GDALDataset *CPHDDataset::OpenMultiDim(GDALOpenInfo *poOpenInfo)
480 :
481 : {
482 6 : auto poDS = std::make_unique<CPHDDataset>();
483 : auto poShared = std::make_shared<CPHDSharedResources>(
484 6 : poOpenInfo->pszFilename, poOpenInfo->fpL);
485 3 : poOpenInfo->fpL = nullptr;
486 : auto poRootGroup =
487 6 : std::make_shared<CPHDGroup>(std::string(), "/", poShared);
488 :
489 3 : poShared->m_fp->Seek(0, SEEK_SET);
490 3 : const char *pszLine = nullptr;
491 :
492 33 : while ((pszLine = CPLReadLineL(poShared->m_fp.get())) != nullptr)
493 : {
494 : // header section terminator (required)
495 33 : if EQUAL (pszLine, "\f")
496 3 : break;
497 :
498 60 : const CPLStringList aosTokens(CSLTokenizeString2(pszLine, " := /", 0));
499 :
500 30 : if (aosTokens.size() == 2)
501 : {
502 29 : if EQUAL (aosTokens[0], "CPHD")
503 3 : poRootGroup->m_apoAttributes.emplace_back(
504 3 : std::make_shared<GDALAttributeString>("/", "cphd_version",
505 6 : aosTokens[1]));
506 26 : else if EQUAL (aosTokens[0], "RELEASE_INFO")
507 2 : poRootGroup->m_apoAttributes.emplace_back(
508 2 : std::make_shared<GDALAttributeString>("/", "release_info",
509 4 : aosTokens[1]));
510 24 : else if EQUAL (aosTokens[0], "CLASSIFICATION")
511 3 : poRootGroup->m_apoAttributes.emplace_back(
512 3 : std::make_shared<GDALAttributeString>("/", "classification",
513 6 : aosTokens[1]));
514 21 : else if EQUAL (aosTokens[0], "XML_BLOCK_SIZE")
515 3 : poShared->nXmlBlockSize = CPLAtoGIntBig(aosTokens[1]);
516 18 : else if EQUAL (aosTokens[0], "XML_BLOCK_BYTE_OFFSET")
517 3 : poShared->nXmlBlockByteOffset =
518 3 : static_cast<GUIntBig>(CPLAtoGIntBig(aosTokens[1]));
519 15 : else if EQUAL (aosTokens[0], "SUPPORT_BLOCK_SIZE")
520 1 : poShared->nSupportBlockSize = CPLAtoGIntBig(aosTokens[1]);
521 14 : else if EQUAL (aosTokens[0], "SUPPORT_BLOCK_BYTE_OFFSET")
522 1 : poShared->nSupportBlockByteOffset =
523 1 : static_cast<GUIntBig>(CPLAtoGIntBig(aosTokens[1]));
524 13 : else if EQUAL (aosTokens[0], "PVP_BLOCK_SIZE")
525 3 : poShared->nPVPBlockSize = CPLAtoGIntBig(aosTokens[1]);
526 10 : else if EQUAL (aosTokens[0], "PVP_BLOCK_BYTE_OFFSET")
527 3 : poShared->nPVPBlockByteOffset =
528 3 : static_cast<GUIntBig>(CPLAtoGIntBig(aosTokens[1]));
529 7 : else if EQUAL (aosTokens[0], "SIGNAL_BLOCK_SIZE")
530 3 : poShared->nSignalBlockSize = CPLAtoGIntBig(aosTokens[1]);
531 4 : else if EQUAL (aosTokens[0], "SIGNAL_BLOCK_BYTE_OFFSET")
532 3 : poShared->nSignalBlockByteOffset =
533 3 : static_cast<GUIntBig>(CPLAtoGIntBig(aosTokens[1]));
534 : else
535 1 : poRootGroup->m_apoAttributes.emplace_back(
536 2 : std::make_shared<GDALAttributeString>(
537 3 : "/", CPLString(aosTokens[0]).tolower(), aosTokens[1]));
538 : }
539 : }
540 :
541 : // read XML block
542 3 : if (poShared->nXmlBlockByteOffset && poShared->nXmlBlockSize)
543 : {
544 : std::map<CPLString, CPLString> oAttrs{
545 : {"collector_name", "CollectionId.CollectorName"},
546 : {"core_name", "CollectionId.CoreName"},
547 : {"collect_type", "CollectionId.CollectType"},
548 18 : {"radar_mode", "CollectionId.RadarMode.ModeType"}};
549 :
550 3 : poShared->m_fp->Seek(poShared->nXmlBlockByteOffset, SEEK_SET);
551 3 : CPLString osBuffer;
552 3 : if (poShared->nXmlBlockSize > std::numeric_limits<int>::max())
553 : {
554 0 : CPLError(CE_Failure, CPLE_AppDefined,
555 : "XML block size is too large for file %s",
556 : poOpenInfo->pszFilename);
557 0 : return nullptr;
558 : }
559 : try
560 : {
561 3 : if (poShared->nXmlBlockSize > 100 * 1024 * 1024)
562 : {
563 : VSIStatBufL sStat;
564 0 : if (VSIStatL(poOpenInfo->pszFilename, &sStat) == 0)
565 : {
566 0 : GIntBig nFileSize = static_cast<GIntBig>(sStat.st_size);
567 0 : if (poShared->nXmlBlockSize > nFileSize)
568 : {
569 0 : CPLError(CE_Failure, CPLE_AppDefined,
570 : "XML block size is too large for file %s",
571 : poOpenInfo->pszFilename);
572 0 : return nullptr;
573 : }
574 : }
575 : }
576 :
577 3 : osBuffer.resize(static_cast<size_t>(poShared->nXmlBlockSize));
578 : }
579 0 : catch (const std::exception &e)
580 : {
581 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
582 0 : return nullptr;
583 : }
584 :
585 6 : poShared->m_fp->Read(&osBuffer[0],
586 3 : static_cast<size_t>(poShared->nXmlBlockSize), 1);
587 :
588 3 : poShared->m_poXMLTree.reset(CPLParseXMLString(osBuffer));
589 :
590 15 : for (auto const &[osName, osPath] : oAttrs)
591 : {
592 : auto pszValue =
593 12 : CPLGetXMLValue(poShared->m_poXMLTree.get(), osPath, nullptr);
594 12 : if (pszValue != nullptr)
595 12 : poRootGroup->m_apoAttributes.emplace_back(
596 12 : std::make_shared<GDALAttributeString>("/", osName,
597 12 : pszValue));
598 : }
599 :
600 : // make the entire metadata xml tree available at the root level
601 3 : if (CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
602 : "INCLUDE_XML", "YES")))
603 : {
604 3 : poRootGroup->m_apoAttributes.emplace_back(
605 3 : std::make_shared<GDALAttributeString>("/", "xml", osBuffer));
606 : }
607 : }
608 : else
609 : {
610 0 : CPLError(CE_Failure, CPLE_AppDefined,
611 : "XML Offset and/or Size not found in CPHD header: %s.",
612 : poOpenInfo->pszFilename);
613 0 : return nullptr;
614 : }
615 :
616 3 : if (!poShared->m_poXMLTree)
617 : {
618 0 : CPLError(CE_Failure, CPLE_AppDefined, "XML block not parsed: %s.",
619 : poOpenInfo->pszFilename);
620 0 : return nullptr;
621 : }
622 :
623 3 : if (poRootGroup->Init())
624 : {
625 3 : poDS->m_poRootGroup = std::move(poRootGroup);
626 3 : poDS->SetDescription(poOpenInfo->pszFilename);
627 :
628 : // Setup/check for pam .aux.xml.
629 3 : poDS->TryLoadXML();
630 :
631 3 : return poDS.release();
632 : }
633 : else
634 0 : return nullptr;
635 : }
636 :
637 : /************************************************************************/
638 : /* Open() */
639 : /************************************************************************/
640 :
641 3 : GDALDataset *CPHDDataset::Open(GDALOpenInfo *poOpenInfo)
642 :
643 : {
644 3 : const auto eIdentify = CPHDDatasetIdentify(poOpenInfo);
645 3 : if (eIdentify == GDAL_IDENTIFY_FALSE)
646 0 : return nullptr;
647 :
648 3 : return CPHDDataset::OpenMultiDim(poOpenInfo);
649 : }
650 :
651 : /************************************************************************/
652 : /* ==================================================================== */
653 : /* CPHDGroup */
654 : /* ==================================================================== */
655 : /************************************************************************/
656 :
657 : /************************************************************************/
658 : /* AddChannel() */
659 : /************************************************************************/
660 3 : std::shared_ptr<CPHDGroup> CPHDGroup::AddChannel(const CPLXMLNode *psChannel)
661 : {
662 : // assign datasource and arrays for this group as there are not many channels
663 3 : const auto pszIdentifier = CPLGetXMLValue(psChannel, "Identifier", "");
664 :
665 : // these are required by the XML schema
666 3 : const auto pszSignalBlockFormat = CPLGetXMLValue(
667 3 : m_poShared->m_poXMLTree.get(), "Data.SignalArrayFormat", nullptr);
668 : const auto pszSignalArrayByteOffset =
669 3 : CPLGetXMLValue(psChannel, "SignalArrayByteOffset", nullptr);
670 : const auto pszSignalArrayWidth =
671 3 : CPLGetXMLValue(psChannel, "NumSamples", nullptr);
672 : const auto pszSignalArrayHeight =
673 3 : CPLGetXMLValue(psChannel, "NumVectors", nullptr);
674 3 : const auto pszNumBytesPVP = CPLGetXMLValue(m_poShared->m_poXMLTree.get(),
675 : "Data.NumBytesPVP", nullptr);
676 : const auto pszPVPArrayByteOffset =
677 3 : CPLGetXMLValue(psChannel, "PVPArrayByteOffset", nullptr);
678 :
679 3 : if ((pszSignalBlockFormat == nullptr) ||
680 3 : (pszSignalArrayByteOffset == nullptr) ||
681 3 : (pszSignalArrayWidth == nullptr) || (pszSignalArrayHeight == nullptr))
682 : {
683 0 : CPLError(CE_Failure, CPLE_AppDefined,
684 : "Required signal block array offsets and "
685 : "format/height/width not found in XML description : %s.",
686 0 : m_poShared->m_osFilename.c_str());
687 0 : return nullptr;
688 : }
689 :
690 3 : if ((pszPVPArrayByteOffset == nullptr) || (pszNumBytesPVP == nullptr))
691 : {
692 0 : CPLError(
693 : CE_Failure, CPLE_AppDefined,
694 : "Required PVP array offsets and "
695 : "number of bytes (NumBytesPVP) not found in XML description : %s.",
696 0 : m_poShared->m_osFilename.c_str());
697 0 : return nullptr;
698 : }
699 :
700 3 : const auto nXSize = atoi(pszSignalArrayWidth);
701 3 : const auto nYSize = atoi(pszSignalArrayHeight);
702 :
703 3 : if (nXSize < 0 || nYSize < 0)
704 : {
705 0 : CPLError(CE_Failure, CPLE_AppDefined,
706 : "Signal block dimensions are incorrect "
707 : "Width %i Height %i : %s.",
708 0 : nXSize, nYSize, m_poShared->m_osFilename.c_str());
709 0 : return nullptr;
710 : }
711 :
712 : const auto poSubGroup =
713 6 : std::make_shared<CPHDGroup>("/", pszIdentifier, m_poShared);
714 :
715 3 : const auto osSignalBlockName("SignalBlock");
716 3 : const GDALDataType dt = ParseComplexDataType(
717 3 : pszSignalBlockFormat, m_poShared->m_osFilename.c_str());
718 :
719 3 : if (dt == GDT_Unknown)
720 : {
721 0 : CPLError(CE_Failure, CPLE_AppDefined,
722 : "Unrecognized complex data type %s : %s.",
723 0 : pszSignalBlockFormat, m_poShared->m_osFilename.c_str());
724 0 : return nullptr;
725 : }
726 :
727 : // add signal block
728 6 : auto poSignalDS = std::make_unique<CPHDInternalDataset>();
729 3 : poSignalDS->nRasterXSize = nXSize;
730 3 : poSignalDS->nRasterYSize = nYSize;
731 :
732 : auto poBand = std::make_unique<CPHDInternalBand>(
733 3 : poSignalDS.get(), 1, m_poShared->m_fp.get(),
734 3 : m_poShared->nSignalBlockByteOffset + atoi(pszSignalArrayByteOffset),
735 3 : GDALGetDataTypeSizeBytes(dt), GDALGetDataTypeSizeBytes(dt) * nXSize,
736 6 : dt);
737 :
738 3 : poSignalDS->SetBand(1, std::move(poBand));
739 : auto poSignalArray = CPHDMDArray::Create(
740 6 : std::move(poSignalDS), std::string(pszIdentifier), osSignalBlockName,
741 15 : GDALExtendedDataType::Create(dt));
742 :
743 3 : poSubGroup->m_apoArrays.emplace_back(std::move(poSignalArray));
744 :
745 : // add PVPs
746 3 : m_poShared->nPVPArrayByteOffset = atoi(pszPVPArrayByteOffset);
747 :
748 : const auto oEDTPvp =
749 3 : ParsePVPDataType(CPLGetXMLNode(m_poShared->m_poXMLTree.get(), "PVP"),
750 9 : m_poShared->m_osFilename.c_str());
751 3 : if (oEDTPvp.GetClass() == GEDTC_NUMERIC &&
752 0 : oEDTPvp.GetNumericDataType() == GDT_Unknown)
753 0 : return nullptr;
754 :
755 : auto poVectorDim = std::make_shared<GDALDimensionWeakIndexingVar>(
756 6 : "/" + std::string(pszIdentifier), "Vector", GDAL_DIM_TYPE_VERTICAL,
757 6 : "AZIMUTH", nYSize);
758 :
759 : auto poPvpArray = MEMMDArray::Create(
760 12 : std::string(pszIdentifier), PVP_ARRAY_NAME, {poVectorDim}, oEDTPvp);
761 3 : poSubGroup->m_apoArrays.emplace_back(std::move(poPvpArray));
762 :
763 : // add group to root
764 3 : m_apoGroups.emplace_back(std::move(poSubGroup));
765 3 : return m_apoGroups.back();
766 : }
767 :
768 : /************************************************************************/
769 : /* AddSupportArray() */
770 : /************************************************************************/
771 2 : bool CPHDGroup::AddSupportArray(const CPLXMLNode *psDataSupportArray)
772 : {
773 : // get support array section
774 : auto psSupportXml =
775 2 : CPLGetXMLNode(m_poShared->m_poXMLTree.get(), "SupportArray");
776 :
777 2 : if (psSupportXml == nullptr)
778 0 : return false;
779 :
780 2 : auto *psSupportChild = psSupportXml->psChild;
781 : const auto pszArrayName =
782 2 : CPLGetXMLValue(psDataSupportArray, "Identifier", "");
783 :
784 : // process only known support arrays for now
785 3 : for (; psSupportChild != nullptr; psSupportChild = psSupportChild->psNext)
786 : {
787 3 : if EQUAL (pszArrayName,
788 : CPLGetXMLValue(psSupportChild, "Identifier", ""))
789 : {
790 2 : const auto pszElementName = psSupportChild->pszValue;
791 0 : std::unique_ptr<CPHDInternalBand> poBand;
792 : const auto pszSupportArrayWidth =
793 2 : CPLGetXMLValue(psDataSupportArray, "NumCols", nullptr);
794 : const auto pszSupportArrayHeight =
795 2 : CPLGetXMLValue(psDataSupportArray, "NumRows", nullptr);
796 : const auto pszArrayByteOffset =
797 2 : CPLGetXMLValue(psDataSupportArray, "ArrayByteOffset", nullptr);
798 :
799 2 : if (!pszSupportArrayWidth || !pszSupportArrayHeight ||
800 : !pszArrayByteOffset)
801 : {
802 0 : CPLError(CE_Failure, CPLE_AppDefined,
803 : "Support Array height/width/offset not found in XML "
804 : "description : %s.",
805 0 : m_poShared->m_osFilename.c_str());
806 0 : return false;
807 : }
808 :
809 2 : int nWidth = atoi(pszSupportArrayWidth);
810 2 : int nHeight = atoi(pszSupportArrayHeight);
811 :
812 2 : if (nWidth < 0 || nHeight < 0)
813 : {
814 0 : CPLError(CE_Failure, CPLE_AppDefined,
815 : "Support array width/height is incorrect "
816 : "width %i height %i for %s",
817 0 : nWidth, nHeight, m_poShared->m_osFilename.c_str());
818 0 : return false;
819 : }
820 :
821 : // add support array
822 2 : auto poSupportDS = std::make_unique<CPHDInternalDataset>();
823 2 : poSupportDS->nRasterXSize = nWidth;
824 2 : poSupportDS->nRasterYSize = nHeight;
825 :
826 2 : if (EQUAL(pszElementName, "AntGainPhase") ||
827 0 : EQUAL(pszElementName, "DwellTimeArray"))
828 : // DataType: F4;F4
829 2 : poBand = std::make_unique<CPHDInternalBand>(
830 2 : poSupportDS.get(), 1, m_poShared->m_fp.get(),
831 0 : m_poShared->nSupportBlockByteOffset +
832 2 : atoi(pszArrayByteOffset),
833 2 : GDALGetDataTypeSizeBytes(GDT_CFloat64),
834 0 : GDALGetDataTypeSizeBytes(GDT_CFloat64) *
835 2 : poSupportDS->nRasterXSize,
836 6 : GDT_CFloat64);
837 0 : else if EQUAL (pszElementName, "IAZArray")
838 : // DataType: IAZ=F4
839 0 : poBand = std::make_unique<CPHDInternalBand>(
840 0 : poSupportDS.get(), 1, m_poShared->m_fp.get(),
841 0 : m_poShared->nSupportBlockByteOffset +
842 0 : atoi(pszArrayByteOffset),
843 0 : GDALGetDataTypeSizeBytes(GDT_Float32),
844 0 : GDALGetDataTypeSizeBytes(GDT_Float32) *
845 0 : poSupportDS->nRasterXSize,
846 0 : GDT_Float32);
847 : else
848 : {
849 0 : CPLError(CE_Failure, CPLE_AppDefined,
850 : "Unsupported Support Array %s : %s.", pszArrayName,
851 0 : m_poShared->m_osFilename.c_str());
852 0 : return false;
853 : }
854 :
855 2 : CPLAssert(poBand);
856 :
857 2 : const auto dt = poBand->GetRasterDataType();
858 2 : poSupportDS->SetBand(1, poBand.release());
859 : auto poSupportArray = CPHDMDArray::Create(
860 6 : std::move(poSupportDS), "", std::string(pszArrayName),
861 10 : GDALExtendedDataType::Create(dt));
862 :
863 2 : poSupportArray->m_apoAttributes.emplace_back(
864 4 : std::make_shared<GDALAttributeString>(
865 4 : "/" + std::string(pszArrayName), "element_format",
866 4 : CPLGetXMLValue(psSupportChild, "ElementFormat", "")));
867 2 : poSupportArray->m_apoAttributes.emplace_back(
868 4 : std::make_shared<GDALAttributeNumeric>(
869 4 : "/" + std::string(pszArrayName), "x_0",
870 4 : CPLAtof(CPLGetXMLValue(psSupportChild, "X0", "0."))));
871 2 : poSupportArray->m_apoAttributes.emplace_back(
872 4 : std::make_shared<GDALAttributeNumeric>(
873 4 : "/" + std::string(pszArrayName), "y_0",
874 4 : CPLAtof(CPLGetXMLValue(psSupportChild, "Y0", "0."))));
875 2 : poSupportArray->m_apoAttributes.emplace_back(
876 4 : std::make_shared<GDALAttributeNumeric>(
877 4 : "/" + std::string(pszArrayName), "xss",
878 4 : CPLAtof(CPLGetXMLValue(psSupportChild, "XSS", "0."))));
879 2 : poSupportArray->m_apoAttributes.emplace_back(
880 4 : std::make_shared<GDALAttributeNumeric>(
881 4 : "/" + std::string(pszArrayName), "yss",
882 4 : CPLAtof(CPLGetXMLValue(psSupportChild, "YSS", "0."))));
883 :
884 2 : m_apoArrays.emplace_back(std::move(poSupportArray));
885 :
886 2 : break;
887 : }
888 : }
889 2 : return true;
890 : }
891 :
892 : /************************************************************************/
893 : /* Init() */
894 : /************************************************************************/
895 3 : bool CPHDGroup::Init()
896 : {
897 3 : if ((m_osName != "/") || (!m_apoArrays.empty()))
898 0 : return false;
899 :
900 3 : auto psDataXml = CPLGetXMLNode(m_poShared->m_poXMLTree.get(), "Data");
901 :
902 3 : if (psDataXml == nullptr)
903 0 : return false;
904 :
905 3 : CPLXMLNode *psChild = psDataXml->psChild;
906 :
907 19 : for (; psChild != nullptr; psChild = psChild->psNext)
908 : {
909 16 : if EQUAL (psChild->pszValue, "Channel")
910 : {
911 3 : std::shared_ptr<CPHDGroup> poSubGroup = AddChannel(psChild);
912 3 : if (!poSubGroup)
913 0 : return false;
914 : }
915 13 : else if EQUAL (psChild->pszValue, "SupportArray")
916 : {
917 2 : if (!AddSupportArray(psChild))
918 0 : return false;
919 : }
920 : }
921 3 : return true;
922 : }
923 :
924 : /************************************************************************/
925 : /* GetMDArrayNames() */
926 : /************************************************************************/
927 :
928 6 : std::vector<std::string> CPHDGroup::GetMDArrayNames(CSLConstList) const
929 : {
930 6 : std::vector<std::string> aosArrayNames;
931 :
932 6 : if (!CheckValidAndErrorOutIfNot())
933 0 : return aosArrayNames;
934 :
935 14 : for (auto const &poArray : m_apoArrays)
936 8 : aosArrayNames.emplace_back(poArray->GetName());
937 :
938 6 : return aosArrayNames;
939 : }
940 :
941 : /************************************************************************/
942 : /* OpenMDArray() */
943 : /************************************************************************/
944 :
945 6 : std::shared_ptr<GDALMDArray> CPHDGroup::OpenMDArray(const std::string &osName,
946 : CSLConstList) const
947 : {
948 6 : if (!CheckValidAndErrorOutIfNot())
949 0 : return nullptr;
950 :
951 9 : for (const auto &poArray : m_apoArrays)
952 : {
953 9 : if (poArray->GetName() == osName)
954 : {
955 6 : if (osName == PVP_ARRAY_NAME)
956 : {
957 : const auto poPVPArray =
958 3 : std::dynamic_pointer_cast<MEMMDArray>(poArray);
959 :
960 3 : if (!poPVPArray)
961 : {
962 0 : CPLError(CE_Failure, CPLE_AppDefined,
963 : "Error opening PVP array : %s",
964 0 : m_poShared->m_osFilename.c_str());
965 0 : return nullptr;
966 : }
967 :
968 3 : if (poPVPArray->IsWritable())
969 : {
970 : // read PVP array
971 3 : if (m_poShared->nPVPArrayByteOffset <
972 3 : std::numeric_limits<uint64_t>::max() -
973 3 : m_poShared->nPVPBlockByteOffset)
974 : {
975 3 : m_poShared->m_fp->Seek(
976 6 : m_poShared->nPVPBlockByteOffset +
977 3 : m_poShared->nPVPArrayByteOffset,
978 3 : SEEK_SET);
979 : }
980 : else
981 : {
982 0 : CPLError(CE_Failure, CPLE_AppDefined,
983 : "Unable to read PVPs for %s, one or both of "
984 : "PVP_BLOCK_SIZE and PVP_BLOCK_BYTE_OFFSET are "
985 : "incorrect.",
986 0 : m_poShared->m_osFilename.c_str());
987 0 : return nullptr;
988 : }
989 :
990 : if constexpr (sizeof(size_t) <
991 : sizeof(m_poShared->nPVPBlockSize))
992 : {
993 : if (m_poShared->nPVPBlockSize >
994 : std::numeric_limits<int>::max())
995 : {
996 : CPLError(CE_Failure, CPLE_AppDefined,
997 : "PVP block size is too large for file %s",
998 : m_poShared->m_osFilename.c_str());
999 : return nullptr;
1000 : }
1001 : }
1002 :
1003 : try
1004 : {
1005 3 : if (m_poShared->nPVPBlockSize > 100 * 1024 * 1024)
1006 : {
1007 : // possible to have a very large CPHD file so check file size to continue
1008 : // as PVPs should be relatively small
1009 : VSIStatBufL sStat;
1010 0 : if (VSIStatL(m_poShared->m_osFilename.c_str(),
1011 0 : &sStat) == 0)
1012 : {
1013 0 : GIntBig nFileSize =
1014 : static_cast<GIntBig>(sStat.st_size);
1015 0 : if (m_poShared->nPVPBlockSize > nFileSize)
1016 : {
1017 0 : CPLError(CE_Failure, CPLE_AppDefined,
1018 : "PVP block size is too large for "
1019 : "file %s",
1020 0 : m_poShared->m_osFilename.c_str());
1021 0 : return nullptr;
1022 : }
1023 : }
1024 : else
1025 : {
1026 0 : CPLError(CE_Failure, CPLE_AppDefined,
1027 : "Unable to determine file size for %s",
1028 0 : m_poShared->m_osFilename.c_str());
1029 0 : return nullptr;
1030 : }
1031 : }
1032 6 : m_abyPVPData.resize(
1033 3 : static_cast<size_t>(m_poShared->nPVPBlockSize));
1034 : }
1035 0 : catch (const std::exception &)
1036 : {
1037 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1038 : "Out of memory allocating PVP buffer");
1039 0 : return nullptr;
1040 : }
1041 6 : if (m_poShared->m_fp->Read(
1042 3 : m_abyPVPData.data(),
1043 3 : static_cast<size_t>(m_poShared->nPVPBlockSize)) !=
1044 3 : static_cast<size_t>(m_poShared->nPVPBlockSize))
1045 : {
1046 0 : CPLError(CE_Failure, CPLE_AppDefined,
1047 : "Unable to read PVPs from %s",
1048 0 : m_poShared->m_osFilename.c_str());
1049 0 : return nullptr;
1050 : }
1051 :
1052 : size_t nVectors = static_cast<size_t>(
1053 3 : poPVPArray->GetDimensions()[0]->GetSize());
1054 :
1055 3 : const auto &oEDT = poPVPArray->GetDataType();
1056 : #if CPL_IS_LSB
1057 : // swap all numeric types as CPHD binary data is big endian
1058 3 : auto ptr = m_abyPVPData.data();
1059 :
1060 11353700 : auto swapPtr = [](uint8_t *p, GDALDataType dt)
1061 : {
1062 11353700 : switch (dt)
1063 : {
1064 0 : case GDT_Int16:
1065 : case GDT_UInt16:
1066 : case GDT_Float16:
1067 0 : CPL_SWAP16PTR(p);
1068 0 : break;
1069 0 : case GDT_Int32:
1070 : case GDT_UInt32:
1071 : case GDT_Float32:
1072 0 : CPL_SWAP32PTR(p);
1073 0 : break;
1074 11353700 : case GDT_Int64:
1075 : case GDT_UInt64:
1076 : case GDT_Float64:
1077 11353700 : CPL_SWAP64PTR(p);
1078 11353700 : break;
1079 : // complex
1080 0 : case GDT_CInt16:
1081 : case GDT_CFloat16:
1082 0 : CPL_SWAP16PTR(p);
1083 0 : CPL_SWAP16PTR(p + 2);
1084 0 : break;
1085 0 : case GDT_CInt32:
1086 : case GDT_CFloat32:
1087 0 : CPL_SWAP32PTR(p);
1088 0 : CPL_SWAP32PTR(p + 4);
1089 0 : break;
1090 0 : case GDT_CFloat64:
1091 0 : CPL_SWAP64PTR(p);
1092 0 : CPL_SWAP64PTR(p + 8);
1093 0 : break;
1094 0 : default:
1095 0 : break;
1096 : }
1097 :
1098 11353700 : return GDALGetDataTypeSizeBytes(dt);
1099 : };
1100 :
1101 : // loop over all vectors
1102 346624 : for (size_t i = 0; i < nVectors; i++)
1103 : {
1104 8234060 : for (const auto &comp : oEDT.GetComponents())
1105 : {
1106 7887440 : if (comp->GetType().GetClass() == GEDTC_COMPOUND)
1107 : {
1108 : // swap XYZ and similar one level down types
1109 5199330 : for (const auto &subComp :
1110 6932440 : comp->GetType().GetComponents())
1111 : {
1112 5199330 : ptr +=
1113 5199330 : swapPtr(ptr, subComp->GetType()
1114 : .GetNumericDataType());
1115 : }
1116 : }
1117 : else
1118 : {
1119 6154330 : ptr += swapPtr(
1120 6154330 : ptr, comp->GetType().GetNumericDataType());
1121 : }
1122 : }
1123 : }
1124 : #endif
1125 3 : if (!poPVPArray->Init(m_abyPVPData.data()))
1126 : {
1127 0 : CPLError(CE_Failure, CPLE_AppDefined,
1128 : "Unable to read PVP data.");
1129 0 : return nullptr;
1130 : }
1131 :
1132 3 : poPVPArray->SetWritable(false);
1133 : }
1134 : }
1135 6 : return poArray;
1136 : }
1137 : }
1138 0 : return nullptr;
1139 : }
1140 :
1141 : /************************************************************************/
1142 : /* GetGroupNames() */
1143 : /************************************************************************/
1144 : std::vector<std::string>
1145 3 : CPHDGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
1146 :
1147 : {
1148 3 : std::vector<std::string> aosGroupNames;
1149 :
1150 6 : for (auto const &poGroup : m_apoGroups)
1151 3 : aosGroupNames.emplace_back(poGroup->GetName());
1152 :
1153 3 : return aosGroupNames;
1154 : }
1155 :
1156 : /************************************************************************/
1157 : /* OpenGroup() */
1158 : /************************************************************************/
1159 3 : std::shared_ptr<GDALGroup> CPHDGroup::OpenGroup(const std::string &osName,
1160 : CSLConstList) const
1161 :
1162 : {
1163 3 : for (const auto &poGroup : m_apoGroups)
1164 3 : if (poGroup->GetName() == osName)
1165 3 : return poGroup;
1166 :
1167 0 : return nullptr;
1168 : }
1169 :
1170 : /************************************************************************/
1171 : /* CPHDDatasetIdentify() */
1172 : /************************************************************************/
1173 :
1174 53629 : static int CPHDDatasetIdentify(GDALOpenInfo *poOpenInfo)
1175 : {
1176 53635 : return poOpenInfo->IsExtensionEqualToCI("cphd") && poOpenInfo->bStatOK &&
1177 53641 : (poOpenInfo->eAccess == GA_ReadOnly) &&
1178 53635 : (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER);
1179 : }
1180 :
1181 : /************************************************************************/
1182 : /* GDALRegister_CPHD() */
1183 : /************************************************************************/
1184 :
1185 2066 : void GDALRegister_CPHD()
1186 :
1187 : {
1188 2066 : if (GDALGetDriverByName("CPHD") != nullptr)
1189 263 : return;
1190 :
1191 1803 : GDALDriver *poDriver = new GDALDriver();
1192 :
1193 1803 : poDriver->SetDescription("CPHD");
1194 1803 : poDriver->SetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER, "YES");
1195 1803 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1196 1803 : "Compensated Phase History Data Reader");
1197 1803 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/cphd.html");
1198 1803 : poDriver->SetMetadataItem(
1199 : GDAL_DMD_OPENOPTIONLIST,
1200 : "<OpenOptionList>"
1201 : "<Option name='INCLUDE_XML' type='boolean' "
1202 : "description='Whether to include the XML string as a group attribute' "
1203 : "default='YES'/>"
1204 1803 : "</OpenOptionList>");
1205 1803 : poDriver->pfnOpen = CPHDDataset::Open;
1206 1803 : poDriver->pfnIdentify = CPHDDatasetIdentify;
1207 :
1208 1803 : GetGDALDriverManager()->RegisterDriver(poDriver);
1209 : }
|