Line data Source code
1 : /******************************************************************************
2 : *
3 : * Purpose: ADRG reader
4 : * Author: Even Rouault, even.rouault at spatialys.com
5 : *
6 : ******************************************************************************
7 : * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include "cpl_string.h"
13 : #include "gdal_pam.h"
14 : #include "gdal_frmts.h"
15 : #include "gdal_driver.h"
16 : #include "gdal_drivermanager.h"
17 : #include "gdal_openinfo.h"
18 : #include "gdal_cpp_functions.h"
19 : #include "iso8211.h"
20 : #include "ogr_spatialref.h"
21 :
22 : #include <limits>
23 : #include <new>
24 :
25 : #define N_ELEMENTS(x) (sizeof(x) / sizeof(x[0]))
26 :
27 : #define DIGIT_ZERO '0'
28 :
29 : class ADRGDataset final : public GDALPamDataset
30 : {
31 : friend class ADRGRasterBand;
32 :
33 : CPLString osGENFileName;
34 : CPLString osIMGFileName;
35 : OGRSpatialReference m_oSRS{};
36 :
37 : VSILFILE *fdIMG;
38 : int *TILEINDEX;
39 : int offsetInIMG;
40 : int NFC;
41 : int NFL;
42 : double LSO;
43 : double PSO;
44 : int ARV;
45 : int BRV;
46 :
47 : char **papszSubDatasets;
48 :
49 : GDALGeoTransform m_gt{};
50 :
51 : static char **GetGENListFromTHF(const char *pszFileName);
52 : static char **GetIMGListFromGEN(const char *pszFileName,
53 : int *pnRecordIndex = nullptr);
54 : static ADRGDataset *OpenDataset(const char *pszGENFileName,
55 : const char *pszIMGFileName,
56 : DDFRecord *record = nullptr);
57 : static DDFRecord *FindRecordInGENForIMG(DDFModule &module,
58 : const char *pszGENFileName,
59 : const char *pszIMGFileName);
60 :
61 : public:
62 : ADRGDataset();
63 : ~ADRGDataset() override;
64 :
65 : const OGRSpatialReference *GetSpatialRef() const override;
66 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
67 :
68 : char **GetMetadataDomainList() override;
69 : char **GetMetadata(const char *pszDomain = "") override;
70 :
71 : char **GetFileList() override;
72 :
73 : void AddSubDataset(const char *pszGENFileName, const char *pszIMGFileName);
74 :
75 : static GDALDataset *Open(GDALOpenInfo *);
76 :
77 : static double GetLongitudeFromString(const char *str);
78 : static double GetLatitudeFromString(const char *str);
79 : };
80 :
81 : /************************************************************************/
82 : /* ==================================================================== */
83 : /* ADRGRasterBand */
84 : /* ==================================================================== */
85 : /************************************************************************/
86 :
87 : class ADRGRasterBand final : public GDALPamRasterBand
88 : {
89 : friend class ADRGDataset;
90 :
91 : public:
92 : ADRGRasterBand(ADRGDataset *, int);
93 :
94 : GDALColorInterp GetColorInterpretation() override;
95 : CPLErr IReadBlock(int, int, void *) override;
96 :
97 : double GetNoDataValue(int *pbSuccess = nullptr) override;
98 :
99 : // virtual int GetOverviewCount();
100 : // virtual GDALRasterBand* GetOverview(int i);
101 : };
102 :
103 : /************************************************************************/
104 : /* ADRGRasterBand() */
105 : /************************************************************************/
106 :
107 24 : ADRGRasterBand::ADRGRasterBand(ADRGDataset *poDSIn, int nBandIn)
108 :
109 : {
110 24 : poDS = poDSIn;
111 24 : nBand = nBandIn;
112 :
113 24 : eDataType = GDT_Byte;
114 :
115 24 : nBlockXSize = 128;
116 24 : nBlockYSize = 128;
117 24 : }
118 :
119 : /************************************************************************/
120 : /* GetNoDataValue() */
121 : /************************************************************************/
122 :
123 0 : double ADRGRasterBand::GetNoDataValue(int *pbSuccess)
124 : {
125 0 : if (pbSuccess)
126 0 : *pbSuccess = TRUE;
127 :
128 0 : return 0.0;
129 : }
130 :
131 : /************************************************************************/
132 : /* GetColorInterpretation() */
133 : /************************************************************************/
134 :
135 0 : GDALColorInterp ADRGRasterBand::GetColorInterpretation()
136 :
137 : {
138 0 : if (nBand == 1)
139 0 : return GCI_RedBand;
140 :
141 0 : else if (nBand == 2)
142 0 : return GCI_GreenBand;
143 :
144 0 : return GCI_BlueBand;
145 : }
146 :
147 : /************************************************************************/
148 : /* IReadBlock() */
149 : /************************************************************************/
150 :
151 4 : CPLErr ADRGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
152 :
153 : {
154 4 : ADRGDataset *l_poDS = cpl::down_cast<ADRGDataset *>(poDS);
155 4 : int nBlock = nBlockYOff * l_poDS->NFC + nBlockXOff;
156 4 : if (nBlockXOff >= l_poDS->NFC || nBlockYOff >= l_poDS->NFL)
157 : {
158 0 : CPLError(CE_Failure, CPLE_AppDefined,
159 : "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d", nBlockXOff,
160 : l_poDS->NFC, nBlockYOff, l_poDS->NFL);
161 0 : return CE_Failure;
162 : }
163 4 : CPLDebug("ADRG", "(%d,%d) -> nBlock = %d", nBlockXOff, nBlockYOff, nBlock);
164 :
165 : vsi_l_offset offset;
166 4 : if (l_poDS->TILEINDEX)
167 : {
168 4 : if (l_poDS->TILEINDEX[nBlock] <= 0)
169 : {
170 0 : memset(pImage, 0, 128 * 128);
171 0 : return CE_None;
172 : }
173 4 : offset = l_poDS->offsetInIMG +
174 4 : static_cast<vsi_l_offset>(l_poDS->TILEINDEX[nBlock] - 1) *
175 4 : 128 * 128 * 3 +
176 4 : (nBand - 1) * 128 * 128;
177 : }
178 : else
179 0 : offset = l_poDS->offsetInIMG +
180 0 : static_cast<vsi_l_offset>(nBlock) * 128 * 128 * 3 +
181 0 : (nBand - 1) * 128 * 128;
182 :
183 4 : if (VSIFSeekL(l_poDS->fdIMG, offset, SEEK_SET) != 0)
184 : {
185 0 : CPLError(CE_Failure, CPLE_FileIO,
186 : "Cannot seek to offset " CPL_FRMT_GUIB, offset);
187 0 : return CE_Failure;
188 : }
189 4 : if (VSIFReadL(pImage, 1, 128 * 128, l_poDS->fdIMG) != 128 * 128)
190 : {
191 0 : CPLError(CE_Failure, CPLE_FileIO,
192 : "Cannot read data at offset " CPL_FRMT_GUIB, offset);
193 0 : return CE_Failure;
194 : }
195 :
196 4 : return CE_None;
197 : }
198 :
199 : /************************************************************************/
200 : /* ADRGDataset() */
201 : /************************************************************************/
202 :
203 9 : ADRGDataset::ADRGDataset()
204 : : fdIMG(nullptr), TILEINDEX(nullptr), offsetInIMG(0), NFC(0), NFL(0),
205 9 : LSO(0.0), PSO(0.0), ARV(0), BRV(0), papszSubDatasets(nullptr)
206 : {
207 9 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
208 9 : }
209 :
210 : /************************************************************************/
211 : /* ~ADRGDataset() */
212 : /************************************************************************/
213 :
214 18 : ADRGDataset::~ADRGDataset()
215 : {
216 9 : CSLDestroy(papszSubDatasets);
217 :
218 9 : if (fdIMG)
219 : {
220 8 : VSIFCloseL(fdIMG);
221 : }
222 :
223 9 : if (TILEINDEX)
224 : {
225 8 : delete[] TILEINDEX;
226 : }
227 18 : }
228 :
229 : /************************************************************************/
230 : /* GetFileList() */
231 : /************************************************************************/
232 :
233 3 : char **ADRGDataset::GetFileList()
234 : {
235 3 : char **papszFileList = GDALPamDataset::GetFileList();
236 :
237 3 : if (!osGENFileName.empty() && !osIMGFileName.empty())
238 : {
239 3 : CPLString osMainFilename = GetDescription();
240 : VSIStatBufL sStat;
241 :
242 3 : const bool bMainFileReal = VSIStatL(osMainFilename, &sStat) == 0;
243 3 : if (bMainFileReal)
244 : {
245 4 : CPLString osShortMainFilename = CPLGetFilename(osMainFilename);
246 4 : CPLString osShortGENFileName = CPLGetFilename(osGENFileName);
247 2 : if (!EQUAL(osShortMainFilename.c_str(), osShortGENFileName.c_str()))
248 : papszFileList =
249 1 : CSLAddString(papszFileList, osGENFileName.c_str());
250 : }
251 : else
252 1 : papszFileList = CSLAddString(papszFileList, osGENFileName.c_str());
253 :
254 3 : papszFileList = CSLAddString(papszFileList, osIMGFileName.c_str());
255 : }
256 :
257 3 : return papszFileList;
258 : }
259 :
260 : /************************************************************************/
261 : /* AddSubDataset() */
262 : /************************************************************************/
263 :
264 2 : void ADRGDataset::AddSubDataset(const char *pszGENFileName,
265 : const char *pszIMGFileName)
266 : {
267 : char szName[80];
268 2 : int nCount = CSLCount(papszSubDatasets) / 2;
269 :
270 2 : CPLString osSubDatasetName;
271 2 : osSubDatasetName = "ADRG:";
272 2 : osSubDatasetName += pszGENFileName;
273 2 : osSubDatasetName += ",";
274 2 : osSubDatasetName += pszIMGFileName;
275 :
276 2 : snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
277 2 : papszSubDatasets =
278 2 : CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
279 :
280 2 : snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
281 2 : papszSubDatasets =
282 2 : CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
283 2 : }
284 :
285 : /************************************************************************/
286 : /* GetMetadataDomainList() */
287 : /************************************************************************/
288 :
289 0 : char **ADRGDataset::GetMetadataDomainList()
290 : {
291 0 : return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
292 0 : TRUE, "SUBDATASETS", nullptr);
293 : }
294 :
295 : /************************************************************************/
296 : /* GetMetadata() */
297 : /************************************************************************/
298 :
299 1 : char **ADRGDataset::GetMetadata(const char *pszDomain)
300 :
301 : {
302 1 : if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
303 0 : return papszSubDatasets;
304 :
305 1 : return GDALPamDataset::GetMetadata(pszDomain);
306 : }
307 :
308 : /************************************************************************/
309 : /* GetSpatialRef() */
310 : /************************************************************************/
311 :
312 2 : const OGRSpatialReference *ADRGDataset::GetSpatialRef() const
313 : {
314 2 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
315 : }
316 :
317 : /************************************************************************/
318 : /* GetGeoTransform() */
319 : /************************************************************************/
320 :
321 2 : CPLErr ADRGDataset::GetGeoTransform(GDALGeoTransform >) const
322 : {
323 2 : if (papszSubDatasets != nullptr)
324 0 : return CE_Failure;
325 :
326 2 : gt = m_gt;
327 :
328 2 : return CE_None;
329 : }
330 :
331 : /************************************************************************/
332 : /* GetLongitudeFromString() */
333 : /************************************************************************/
334 :
335 8 : double ADRGDataset::GetLongitudeFromString(const char *str)
336 : {
337 8 : char ddd[3 + 1] = {0};
338 8 : char mm[2 + 1] = {0};
339 8 : char ssdotss[5 + 1] = {0};
340 8 : int sign = (str[0] == '+') ? 1 : -1;
341 8 : str++;
342 8 : strncpy(ddd, str, 3);
343 8 : str += 3;
344 8 : strncpy(mm, str, 2);
345 8 : str += 2;
346 8 : strncpy(ssdotss, str, 5);
347 8 : return sign * (CPLAtof(ddd) + CPLAtof(mm) / 60 + CPLAtof(ssdotss) / 3600);
348 : }
349 :
350 : /************************************************************************/
351 : /* GetLatitudeFromString() */
352 : /************************************************************************/
353 :
354 8 : double ADRGDataset::GetLatitudeFromString(const char *str)
355 : {
356 8 : char ddd[2 + 1] = {0};
357 8 : char mm[2 + 1] = {0};
358 8 : char ssdotss[5 + 1] = {0};
359 8 : int sign = (str[0] == '+') ? 1 : -1;
360 8 : str++;
361 8 : strncpy(ddd, str, 2);
362 8 : str += 2;
363 8 : strncpy(mm, str, 2);
364 8 : str += 2;
365 8 : strncpy(ssdotss, str, 5);
366 8 : return sign * (CPLAtof(ddd) + CPLAtof(mm) / 60 + CPLAtof(ssdotss) / 3600);
367 : }
368 :
369 : /************************************************************************/
370 : /* FindRecordInGENForIMG() */
371 : /************************************************************************/
372 :
373 2 : DDFRecord *ADRGDataset::FindRecordInGENForIMG(DDFModule &module,
374 : const char *pszGENFileName,
375 : const char *pszIMGFileName)
376 : {
377 : /* Finds the GEN file corresponding to the IMG file */
378 2 : if (!module.Open(pszGENFileName, TRUE))
379 0 : return nullptr;
380 :
381 4 : CPLString osShortIMGFilename = CPLGetFilename(pszIMGFileName);
382 :
383 : /* Now finds the record */
384 : while (true)
385 : {
386 7 : CPLPushErrorHandler(CPLQuietErrorHandler);
387 7 : DDFRecord *record = module.ReadRecord();
388 7 : CPLPopErrorHandler();
389 7 : CPLErrorReset();
390 7 : if (record == nullptr)
391 0 : return nullptr;
392 :
393 7 : if (record->GetFieldCount() >= 5)
394 : {
395 5 : DDFField *field = record->GetField(0);
396 5 : DDFFieldDefn *fieldDefn = field->GetFieldDefn();
397 10 : if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
398 5 : fieldDefn->GetSubfieldCount() == 2))
399 : {
400 2 : continue;
401 : }
402 :
403 5 : const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
404 5 : if (RTY == nullptr)
405 0 : continue;
406 : /* Ignore overviews */
407 5 : if (strcmp(RTY, "OVV") == 0)
408 2 : continue;
409 :
410 3 : if (strcmp(RTY, "GIN") != 0)
411 0 : continue;
412 :
413 3 : field = record->GetField(3);
414 3 : fieldDefn = field->GetFieldDefn();
415 :
416 6 : if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
417 3 : fieldDefn->GetSubfieldCount() == 15))
418 : {
419 0 : continue;
420 : }
421 :
422 3 : const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
423 3 : if (pszBAD == nullptr || strlen(pszBAD) != 12)
424 0 : continue;
425 3 : CPLString osBAD = pszBAD;
426 : {
427 3 : char *c = (char *)strchr(osBAD.c_str(), ' ');
428 3 : if (c)
429 0 : *c = 0;
430 : }
431 :
432 3 : if (EQUAL(osShortIMGFilename.c_str(), osBAD.c_str()))
433 : {
434 2 : return record;
435 : }
436 : }
437 5 : }
438 : }
439 :
440 : /************************************************************************/
441 : /* OpenDataset() */
442 : /************************************************************************/
443 :
444 8 : ADRGDataset *ADRGDataset::OpenDataset(const char *pszGENFileName,
445 : const char *pszIMGFileName,
446 : DDFRecord *record)
447 : {
448 16 : DDFModule module;
449 :
450 8 : int SCA = 0;
451 8 : int ZNA = 0;
452 : double PSP;
453 : int ARV;
454 : int BRV;
455 : double LSO;
456 : double PSO;
457 : int NFL;
458 : int NFC;
459 16 : CPLString osBAD;
460 : int TIF;
461 8 : int *TILEINDEX = nullptr;
462 :
463 8 : if (record == nullptr)
464 : {
465 2 : record = FindRecordInGENForIMG(module, pszGENFileName, pszIMGFileName);
466 2 : if (record == nullptr)
467 0 : return nullptr;
468 : }
469 :
470 8 : DDFField *field = record->GetField(1);
471 8 : if (field == nullptr)
472 0 : return nullptr;
473 8 : DDFFieldDefn *fieldDefn = field->GetFieldDefn();
474 :
475 16 : if (!(strcmp(fieldDefn->GetName(), "DSI") == 0 &&
476 8 : fieldDefn->GetSubfieldCount() == 2))
477 : {
478 0 : return nullptr;
479 : }
480 :
481 8 : const char *pszPTR = record->GetStringSubfield("DSI", 0, "PRT", 0);
482 8 : if (pszPTR == nullptr || !EQUAL(pszPTR, "ADRG"))
483 0 : return nullptr;
484 :
485 8 : const char *pszNAM = record->GetStringSubfield("DSI", 0, "NAM", 0);
486 8 : if (pszNAM == nullptr || strlen(pszNAM) != 8)
487 0 : return nullptr;
488 16 : CPLString osNAM = pszNAM;
489 :
490 8 : field = record->GetField(2);
491 8 : if (field == nullptr)
492 0 : return nullptr;
493 8 : fieldDefn = field->GetFieldDefn();
494 :
495 : // TODO: Support on GIN things. And what is GIN?
496 : // GIN might mean general information and might be a typo of GEN.
497 : // if( isGIN )
498 : {
499 16 : if (!(strcmp(fieldDefn->GetName(), "GEN") == 0 &&
500 8 : fieldDefn->GetSubfieldCount() == 21))
501 : {
502 0 : return nullptr;
503 : }
504 :
505 8 : if (record->GetIntSubfield("GEN", 0, "STR", 0) != 3)
506 0 : return nullptr;
507 :
508 8 : SCA = record->GetIntSubfield("GEN", 0, "SCA", 0);
509 8 : CPLDebug("ADRG", "SCA=%d", SCA);
510 :
511 8 : ZNA = record->GetIntSubfield("GEN", 0, "ZNA", 0);
512 8 : CPLDebug("ADRG", "ZNA=%d", ZNA);
513 :
514 8 : PSP = record->GetFloatSubfield("GEN", 0, "PSP", 0);
515 8 : CPLDebug("ADRG", "PSP=%f", PSP);
516 :
517 8 : ARV = record->GetIntSubfield("GEN", 0, "ARV", 0);
518 8 : CPLDebug("ADRG", "ARV=%d", ARV);
519 :
520 8 : BRV = record->GetIntSubfield("GEN", 0, "BRV", 0);
521 8 : CPLDebug("ADRG", "BRV=%d", BRV);
522 8 : if (ARV <= 0 || (ZNA != 9 && ZNA != 18 && BRV <= 0))
523 0 : return nullptr;
524 :
525 8 : const char *pszLSO = record->GetStringSubfield("GEN", 0, "LSO", 0);
526 8 : if (pszLSO == nullptr || strlen(pszLSO) != 11)
527 0 : return nullptr;
528 8 : LSO = GetLongitudeFromString(pszLSO);
529 8 : CPLDebug("ADRG", "LSO=%f", LSO);
530 :
531 8 : const char *pszPSO = record->GetStringSubfield("GEN", 0, "PSO", 0);
532 8 : if (pszPSO == nullptr || strlen(pszPSO) != 10)
533 0 : return nullptr;
534 8 : PSO = GetLatitudeFromString(pszPSO);
535 8 : CPLDebug("ADRG", "PSO=%f", PSO);
536 : }
537 : #if 0
538 : else
539 : {
540 : if( !(strcmp(fieldDefn->GetName(), "OVI") == 0 &&
541 : fieldDefn->GetSubfieldCount() == 5) )
542 : {
543 : return NULL;
544 : }
545 :
546 : if( record->GetIntSubfield("OVI", 0, "STR", 0) != 3 )
547 : return NULL;
548 :
549 : ARV = record->GetIntSubfield("OVI", 0, "ARV", 0);
550 : CPLDebug("ADRG", "ARV=%d", ARV);
551 :
552 : BRV = record->GetIntSubfield("OVI", 0, "BRV", 0);
553 : CPLDebug("ADRG", "BRV=%d", BRV);
554 :
555 : const char* pszLSO = record->GetStringSubfield("OVI", 0, "LSO", 0);
556 : if( pszLSO == NULL || strlen(pszLSO) != 11 )
557 : return NULL;
558 : LSO = GetLongitudeFromString(pszLSO);
559 : CPLDebug("ADRG", "LSO=%f", LSO);
560 :
561 : const char* pszPSO = record->GetStringSubfield("OVI", 0, "PSO", 0);
562 : if( pszPSO == NULL || strlen(pszPSO) != 10 )
563 : return NULL;
564 : PSO = GetLatitudeFromString(pszPSO);
565 : CPLDebug("ADRG", "PSO=%f", PSO);
566 : }
567 : #endif
568 :
569 8 : field = record->GetField(3);
570 8 : if (field == nullptr)
571 0 : return nullptr;
572 8 : fieldDefn = field->GetFieldDefn();
573 :
574 16 : if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
575 8 : fieldDefn->GetSubfieldCount() == 15))
576 : {
577 0 : return nullptr;
578 : }
579 :
580 8 : NFL = record->GetIntSubfield("SPR", 0, "NFL", 0);
581 8 : CPLDebug("ADRG", "NFL=%d", NFL);
582 :
583 8 : NFC = record->GetIntSubfield("SPR", 0, "NFC", 0);
584 8 : CPLDebug("ADRG", "NFC=%d", NFC);
585 :
586 8 : const auto knIntMax = std::numeric_limits<int>::max();
587 8 : if (NFL <= 0 || NFC <= 0 || NFL > knIntMax / 128 || NFC > knIntMax / 128 ||
588 8 : NFL > (knIntMax - 1) / (NFC * 5))
589 : {
590 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid NFL / NFC values");
591 0 : return nullptr;
592 : }
593 :
594 8 : int PNC = record->GetIntSubfield("SPR", 0, "PNC", 0);
595 8 : CPLDebug("ADRG", "PNC=%d", PNC);
596 8 : if (PNC != 128)
597 : {
598 0 : return nullptr;
599 : }
600 :
601 8 : int PNL = record->GetIntSubfield("SPR", 0, "PNL", 0);
602 8 : CPLDebug("ADRG", "PNL=%d", PNL);
603 8 : if (PNL != 128)
604 : {
605 0 : return nullptr;
606 : }
607 :
608 8 : const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
609 8 : if (pszBAD == nullptr || strlen(pszBAD) != 12)
610 0 : return nullptr;
611 8 : osBAD = pszBAD;
612 : {
613 8 : char *c = (char *)strchr(osBAD.c_str(), ' ');
614 8 : if (c)
615 0 : *c = 0;
616 : }
617 8 : CPLDebug("ADRG", "BAD=%s", osBAD.c_str());
618 :
619 8 : const DDFSubfieldDefn *subfieldDefn = fieldDefn->GetSubfield(14);
620 16 : if (!(strcmp(subfieldDefn->GetName(), "TIF") == 0 &&
621 8 : (subfieldDefn->GetFormat())[0] == 'A'))
622 : {
623 0 : return nullptr;
624 : }
625 :
626 8 : const char *pszTIF = record->GetStringSubfield("SPR", 0, "TIF", 0);
627 8 : if (pszTIF == nullptr)
628 0 : return nullptr;
629 8 : TIF = pszTIF[0] == 'Y';
630 8 : CPLDebug("ADRG", "TIF=%d", TIF);
631 :
632 8 : if (TIF)
633 : {
634 8 : if (record->GetFieldCount() != 6)
635 : {
636 0 : return nullptr;
637 : }
638 :
639 8 : field = record->GetField(5);
640 8 : if (field == nullptr)
641 0 : return nullptr;
642 8 : fieldDefn = field->GetFieldDefn();
643 :
644 8 : if (!(strcmp(fieldDefn->GetName(), "TIM") == 0))
645 : {
646 0 : return nullptr;
647 : }
648 :
649 8 : if (field->GetDataSize() != 5 * NFL * NFC + 1)
650 : {
651 0 : return nullptr;
652 : }
653 :
654 : try
655 : {
656 8 : TILEINDEX = new int[NFL * NFC];
657 : }
658 0 : catch (const std::exception &)
659 : {
660 0 : return nullptr;
661 : }
662 8 : const char *ptr = field->GetData();
663 8 : char offset[5 + 1] = {0};
664 16 : for (int i = 0; i < NFL * NFC; i++)
665 : {
666 8 : strncpy(offset, ptr, 5);
667 8 : ptr += 5;
668 8 : TILEINDEX[i] = atoi(offset);
669 : // CPLDebug("ADRG", "TSI[%d]=%d", i, TILEINDEX[i]);
670 : }
671 : }
672 :
673 8 : VSILFILE *fdIMG = VSIFOpenL(pszIMGFileName, "rb");
674 8 : if (fdIMG == nullptr)
675 : {
676 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s\n",
677 : pszIMGFileName);
678 0 : delete[] TILEINDEX;
679 0 : return nullptr;
680 : }
681 :
682 : /* Skip ISO8211 header of IMG file */
683 8 : int offsetInIMG = 0;
684 : char c;
685 : char recordName[3];
686 8 : if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
687 : {
688 0 : VSIFCloseL(fdIMG);
689 0 : delete[] TILEINDEX;
690 0 : return nullptr;
691 : }
692 2064 : while (!VSIFEofL(fdIMG))
693 : {
694 2064 : if (c == 30)
695 : {
696 48 : if (VSIFReadL(recordName, 1, 3, fdIMG) != 3)
697 : {
698 0 : VSIFCloseL(fdIMG);
699 0 : delete[] TILEINDEX;
700 0 : return nullptr;
701 : }
702 48 : offsetInIMG += 3;
703 48 : if (STARTS_WITH(recordName, "IMG"))
704 : {
705 8 : offsetInIMG += 4;
706 16 : if (VSIFSeekL(fdIMG, 3, SEEK_CUR) != 0 ||
707 8 : VSIFReadL(&c, 1, 1, fdIMG) != 1)
708 : {
709 0 : VSIFCloseL(fdIMG);
710 0 : delete[] TILEINDEX;
711 0 : return nullptr;
712 : }
713 14152 : while (c == ' ')
714 : {
715 14144 : offsetInIMG++;
716 14144 : if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
717 : {
718 0 : VSIFCloseL(fdIMG);
719 0 : delete[] TILEINDEX;
720 0 : return nullptr;
721 : }
722 : }
723 8 : offsetInIMG++;
724 8 : break;
725 : }
726 : }
727 :
728 2056 : offsetInIMG++;
729 2056 : if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
730 : {
731 0 : VSIFCloseL(fdIMG);
732 0 : delete[] TILEINDEX;
733 0 : return nullptr;
734 : }
735 : }
736 :
737 8 : if (VSIFEofL(fdIMG))
738 : {
739 0 : VSIFCloseL(fdIMG);
740 0 : delete[] TILEINDEX;
741 0 : return nullptr;
742 : }
743 :
744 8 : CPLDebug("ADRG", "Img offset data = %d", offsetInIMG);
745 :
746 8 : ADRGDataset *poDS = new ADRGDataset();
747 :
748 8 : poDS->osGENFileName = pszGENFileName;
749 8 : poDS->osIMGFileName = pszIMGFileName;
750 8 : poDS->NFC = NFC;
751 8 : poDS->NFL = NFL;
752 8 : poDS->nRasterXSize = NFC * 128;
753 8 : poDS->nRasterYSize = NFL * 128;
754 8 : poDS->LSO = LSO;
755 8 : poDS->PSO = PSO;
756 8 : poDS->ARV = ARV;
757 8 : poDS->BRV = BRV;
758 8 : poDS->TILEINDEX = TILEINDEX;
759 8 : poDS->fdIMG = fdIMG;
760 8 : poDS->offsetInIMG = offsetInIMG;
761 :
762 8 : if (ZNA == 9)
763 : {
764 : // North Polar Case
765 1 : poDS->m_gt[0] = 111319.4907933 * (90.0 - PSO) * sin(LSO * M_PI / 180.0);
766 1 : poDS->m_gt[1] = 40075016.68558 / ARV;
767 1 : poDS->m_gt[2] = 0.0;
768 2 : poDS->m_gt[3] =
769 1 : -111319.4907933 * (90.0 - PSO) * cos(LSO * M_PI / 180.0);
770 1 : poDS->m_gt[4] = 0.0;
771 1 : poDS->m_gt[5] = -40075016.68558 / ARV;
772 1 : poDS->m_oSRS.importFromWkt(
773 : "PROJCS[\"ARC_System_Zone_09\",GEOGCS[\"GCS_Sphere\","
774 : "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
775 : "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
776 : "PROJECTION[\"Azimuthal_Equidistant\"],"
777 : "PARAMETER[\"latitude_of_center\",90],"
778 : "PARAMETER[\"longitude_of_center\",0],"
779 : "PARAMETER[\"false_easting\",0],"
780 : "PARAMETER[\"false_northing\",0],"
781 : "UNIT[\"metre\",1]]");
782 : }
783 7 : else if (ZNA == 18)
784 : {
785 : // South Polar Case
786 1 : poDS->m_gt[0] = 111319.4907933 * (90.0 + PSO) * sin(LSO * M_PI / 180.0);
787 1 : poDS->m_gt[1] = 40075016.68558 / ARV;
788 1 : poDS->m_gt[2] = 0.0;
789 1 : poDS->m_gt[3] = 111319.4907933 * (90.0 + PSO) * cos(LSO * M_PI / 180.0);
790 1 : poDS->m_gt[4] = 0.0;
791 1 : poDS->m_gt[5] = -40075016.68558 / ARV;
792 1 : poDS->m_oSRS.importFromWkt(
793 : "PROJCS[\"ARC_System_Zone_18\",GEOGCS[\"GCS_Sphere\","
794 : "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
795 : "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
796 : "PROJECTION[\"Azimuthal_Equidistant\"],"
797 : "PARAMETER[\"latitude_of_center\",-90],"
798 : "PARAMETER[\"longitude_of_center\",0],"
799 : "PARAMETER[\"false_easting\",0],"
800 : "PARAMETER[\"false_northing\",0],"
801 : "UNIT[\"metre\",1]]");
802 : }
803 : else
804 : {
805 6 : poDS->m_gt[0] = LSO;
806 6 : poDS->m_gt[1] = 360. / ARV;
807 6 : poDS->m_gt[2] = 0.0;
808 6 : poDS->m_gt[3] = PSO;
809 6 : poDS->m_gt[4] = 0.0;
810 6 : poDS->m_gt[5] = -360. / BRV;
811 6 : poDS->m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
812 : }
813 :
814 : // if( isGIN )
815 : {
816 : char szValue[32];
817 8 : snprintf(szValue, sizeof(szValue), "%d", SCA);
818 8 : poDS->SetMetadataItem("ADRG_SCA", szValue);
819 8 : snprintf(szValue, sizeof(szValue), "%d", ZNA);
820 8 : poDS->SetMetadataItem("ADRG_ZNA", szValue);
821 : }
822 :
823 8 : poDS->SetMetadataItem("ADRG_NAM", osNAM.c_str());
824 :
825 8 : poDS->nBands = 3;
826 32 : for (int i = 0; i < poDS->nBands; i++)
827 24 : poDS->SetBand(i + 1, new ADRGRasterBand(poDS, i + 1));
828 :
829 8 : return poDS;
830 : }
831 :
832 : /************************************************************************/
833 : /* GetGENListFromTHF() */
834 : /************************************************************************/
835 :
836 7 : char **ADRGDataset::GetGENListFromTHF(const char *pszFileName)
837 : {
838 14 : DDFModule module;
839 7 : DDFRecord *record = nullptr;
840 7 : int nFilenames = 0;
841 7 : char **papszFileNames = nullptr;
842 :
843 7 : if (!module.Open(pszFileName, TRUE))
844 0 : return papszFileNames;
845 :
846 : while (true)
847 : {
848 27 : CPLPushErrorHandler(CPLQuietErrorHandler);
849 27 : record = module.ReadRecord();
850 27 : CPLPopErrorHandler();
851 27 : CPLErrorReset();
852 27 : if (record == nullptr)
853 7 : break;
854 :
855 20 : if (record->GetFieldCount() >= 2)
856 : {
857 20 : DDFField *field = record->GetField(0);
858 20 : DDFFieldDefn *fieldDefn = field->GetFieldDefn();
859 40 : if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
860 20 : fieldDefn->GetSubfieldCount() == 2))
861 : {
862 0 : continue;
863 : }
864 :
865 20 : const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
866 20 : if (RTY == nullptr || !(strcmp(RTY, "TFN") == 0))
867 : {
868 17 : continue;
869 : }
870 :
871 3 : int iVFFFieldInstance = 0;
872 16 : for (int i = 1; i < record->GetFieldCount(); i++)
873 : {
874 13 : field = record->GetField(i);
875 13 : fieldDefn = field->GetFieldDefn();
876 :
877 26 : if (!(strcmp(fieldDefn->GetName(), "VFF") == 0 &&
878 13 : fieldDefn->GetSubfieldCount() == 1))
879 : {
880 0 : continue;
881 : }
882 :
883 13 : const char *pszVFF = record->GetStringSubfield(
884 : "VFF", iVFFFieldInstance++, "VFF", 0);
885 13 : if (pszVFF == nullptr)
886 0 : continue;
887 13 : CPLString osSubFileName(pszVFF);
888 13 : char *c = (char *)strchr(osSubFileName.c_str(), ' ');
889 13 : if (c)
890 13 : *c = 0;
891 13 : if (EQUAL(CPLGetExtensionSafe(osSubFileName.c_str()).c_str(),
892 : "GEN"))
893 : {
894 3 : CPLDebug("ADRG", "Found GEN file in THF : %s",
895 : osSubFileName.c_str());
896 3 : CPLString osGENFileName(CPLGetDirnameSafe(pszFileName));
897 : char **tokens =
898 3 : CSLTokenizeString2(osSubFileName.c_str(), "/\"", 0);
899 3 : char **ptr = tokens;
900 3 : if (ptr == nullptr)
901 0 : continue;
902 6 : while (*ptr)
903 : {
904 : char **papszDirContent =
905 3 : VSIReadDir(osGENFileName.c_str());
906 3 : char **ptrDir = papszDirContent;
907 3 : if (ptrDir)
908 : {
909 8 : while (*ptrDir)
910 : {
911 8 : if (EQUAL(*ptrDir, *ptr))
912 : {
913 3 : osGENFileName = CPLFormFilenameSafe(
914 : osGENFileName.c_str(), *ptrDir,
915 3 : nullptr);
916 3 : CPLDebug("ADRG",
917 : "Building GEN full file name : %s",
918 : osGENFileName.c_str());
919 3 : break;
920 : }
921 5 : ptrDir++;
922 : }
923 : }
924 3 : if (ptrDir == nullptr)
925 0 : break;
926 3 : CSLDestroy(papszDirContent);
927 3 : ptr++;
928 : }
929 3 : int isNameValid = *ptr == nullptr;
930 3 : CSLDestroy(tokens);
931 3 : if (isNameValid)
932 : {
933 6 : papszFileNames = (char **)CPLRealloc(
934 3 : papszFileNames, sizeof(char *) * (nFilenames + 2));
935 6 : papszFileNames[nFilenames] =
936 3 : CPLStrdup(osGENFileName.c_str());
937 3 : papszFileNames[nFilenames + 1] = nullptr;
938 3 : nFilenames++;
939 : }
940 : }
941 : }
942 : }
943 20 : }
944 7 : return papszFileNames;
945 : }
946 :
947 : /************************************************************************/
948 : /* GetIMGListFromGEN() */
949 : /************************************************************************/
950 :
951 7 : char **ADRGDataset::GetIMGListFromGEN(const char *pszFileName,
952 : int *pnRecordIndex)
953 : {
954 7 : DDFRecord *record = nullptr;
955 14 : CPLStringList aosFilenames;
956 7 : int nRecordIndex = -1;
957 :
958 7 : if (pnRecordIndex)
959 7 : *pnRecordIndex = -1;
960 :
961 14 : DDFModule module;
962 7 : if (!module.Open(pszFileName, TRUE))
963 0 : return nullptr;
964 :
965 : while (true)
966 : {
967 29 : nRecordIndex++;
968 :
969 29 : CPLPushErrorHandler(CPLQuietErrorHandler);
970 29 : record = module.ReadRecord();
971 29 : CPLPopErrorHandler();
972 29 : CPLErrorReset();
973 29 : if (record == nullptr)
974 7 : break;
975 :
976 22 : if (record->GetFieldCount() >= 5)
977 : {
978 15 : DDFField *field = record->GetField(0);
979 15 : DDFFieldDefn *fieldDefn = field->GetFieldDefn();
980 30 : if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
981 15 : fieldDefn->GetSubfieldCount() == 2))
982 : {
983 7 : continue;
984 : }
985 :
986 15 : const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
987 15 : if (RTY == nullptr)
988 0 : continue;
989 : /* Ignore overviews */
990 15 : if (strcmp(RTY, "OVV") == 0)
991 7 : continue;
992 :
993 : // TODO: Fix the non-GIN section or remove it.
994 8 : if (strcmp(RTY, "GIN") != 0)
995 0 : continue;
996 :
997 : /* make sure that the GEN file is part of an ADRG dataset, not a SRP
998 : * dataset, by checking that the GEN field contains a NWO subfield
999 : */
1000 8 : const char *NWO = record->GetStringSubfield("GEN", 0, "NWO", 0);
1001 8 : if (NWO == nullptr)
1002 : {
1003 0 : return nullptr;
1004 : }
1005 :
1006 8 : field = record->GetField(3);
1007 8 : if (field == nullptr)
1008 0 : continue;
1009 8 : fieldDefn = field->GetFieldDefn();
1010 :
1011 16 : if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
1012 8 : fieldDefn->GetSubfieldCount() == 15))
1013 : {
1014 0 : continue;
1015 : }
1016 :
1017 8 : const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
1018 8 : if (pszBAD == nullptr || strlen(pszBAD) != 12)
1019 0 : continue;
1020 8 : std::string osBAD = pszBAD;
1021 8 : const auto nSpacePos = osBAD.find(' ');
1022 8 : if (nSpacePos != std::string::npos)
1023 0 : osBAD.resize(nSpacePos);
1024 8 : CPLDebug("ADRG", "BAD=%s", osBAD.c_str());
1025 8 : if (CPLHasPathTraversal(osBAD.c_str()))
1026 : {
1027 0 : CPLError(CE_Failure, CPLE_AppDefined,
1028 : "Path traversal detected in %s", osBAD.c_str());
1029 0 : return nullptr;
1030 : }
1031 :
1032 : /* Build full IMG file name from BAD value */
1033 16 : CPLString osGENDir(CPLGetDirnameSafe(pszFileName));
1034 :
1035 : std::string osFileName =
1036 16 : CPLFormFilenameSafe(osGENDir.c_str(), osBAD.c_str(), nullptr);
1037 : VSIStatBufL sStatBuf;
1038 8 : if (VSIStatL(osFileName.c_str(), &sStatBuf) == 0)
1039 : {
1040 8 : osBAD = std::move(osFileName);
1041 8 : CPLDebug("ADRG", "Building IMG full file name : %s",
1042 : osBAD.c_str());
1043 : }
1044 : else
1045 : {
1046 0 : char **papszDirContent = nullptr;
1047 0 : if (strcmp(osGENDir.c_str(), "/vsimem") == 0)
1048 : {
1049 0 : CPLString osTmp = osGENDir + "/";
1050 0 : papszDirContent = VSIReadDir(osTmp);
1051 : }
1052 : else
1053 0 : papszDirContent = VSIReadDir(osGENDir);
1054 0 : char **ptrDir = papszDirContent;
1055 0 : while (ptrDir && *ptrDir)
1056 : {
1057 0 : if (EQUAL(*ptrDir, osBAD.c_str()))
1058 : {
1059 0 : osBAD = CPLFormFilenameSafe(osGENDir.c_str(), *ptrDir,
1060 0 : nullptr);
1061 0 : CPLDebug("ADRG", "Building IMG full file name : %s",
1062 : osBAD.c_str());
1063 0 : break;
1064 : }
1065 0 : ptrDir++;
1066 : }
1067 0 : CSLDestroy(papszDirContent);
1068 : }
1069 :
1070 8 : if (aosFilenames.empty() && pnRecordIndex)
1071 7 : *pnRecordIndex = nRecordIndex;
1072 :
1073 8 : aosFilenames.AddString(osBAD.c_str());
1074 : }
1075 22 : }
1076 :
1077 7 : return aosFilenames.StealList();
1078 : }
1079 :
1080 : /************************************************************************/
1081 : /* Open() */
1082 : /************************************************************************/
1083 :
1084 33718 : GDALDataset *ADRGDataset::Open(GDALOpenInfo *poOpenInfo)
1085 : {
1086 33718 : int nRecordIndex = -1;
1087 67432 : CPLString osGENFileName;
1088 67429 : CPLString osIMGFileName;
1089 33716 : bool bFromSubdataset = false;
1090 :
1091 33716 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "ADRG:"))
1092 : {
1093 : char **papszTokens =
1094 2 : CSLTokenizeString2(poOpenInfo->pszFilename + 5, ",", 0);
1095 2 : if (CSLCount(papszTokens) == 2)
1096 : {
1097 2 : osGENFileName = papszTokens[0];
1098 2 : osIMGFileName = papszTokens[1];
1099 2 : bFromSubdataset = true;
1100 : }
1101 2 : CSLDestroy(papszTokens);
1102 : }
1103 : else
1104 : {
1105 33714 : if (poOpenInfo->nHeaderBytes < 500)
1106 31534 : return nullptr;
1107 :
1108 2185 : CPLString osFileName(poOpenInfo->pszFilename);
1109 2185 : if (EQUAL(CPLGetExtensionSafe(osFileName.c_str()).c_str(), "THF"))
1110 : {
1111 7 : char **papszFileNames = GetGENListFromTHF(osFileName.c_str());
1112 7 : if (papszFileNames == nullptr)
1113 4 : return nullptr;
1114 3 : if (papszFileNames[1] == nullptr)
1115 : {
1116 3 : osFileName = papszFileNames[0];
1117 3 : CSLDestroy(papszFileNames);
1118 : }
1119 : else
1120 : {
1121 0 : char **ptr = papszFileNames;
1122 0 : ADRGDataset *poDS = new ADRGDataset();
1123 0 : while (*ptr)
1124 : {
1125 0 : char **papszIMGFileNames = GetIMGListFromGEN(*ptr);
1126 0 : char **papszIMGIter = papszIMGFileNames;
1127 0 : while (papszIMGIter && *papszIMGIter)
1128 : {
1129 0 : poDS->AddSubDataset(*ptr, *papszIMGIter);
1130 0 : papszIMGIter++;
1131 : }
1132 0 : CSLDestroy(papszIMGFileNames);
1133 :
1134 0 : ptr++;
1135 : }
1136 0 : CSLDestroy(papszFileNames);
1137 0 : return poDS;
1138 : }
1139 : }
1140 :
1141 2181 : if (EQUAL(CPLGetExtensionSafe(osFileName.c_str()).c_str(), "GEN"))
1142 : {
1143 7 : osGENFileName = osFileName;
1144 :
1145 : char **papszFileNames =
1146 7 : GetIMGListFromGEN(osFileName.c_str(), &nRecordIndex);
1147 7 : if (papszFileNames == nullptr)
1148 0 : return nullptr;
1149 7 : if (papszFileNames[1] == nullptr)
1150 : {
1151 6 : osIMGFileName = papszFileNames[0];
1152 6 : CSLDestroy(papszFileNames);
1153 : }
1154 : else
1155 : {
1156 1 : char **ptr = papszFileNames;
1157 1 : ADRGDataset *poDS = new ADRGDataset();
1158 3 : while (*ptr)
1159 : {
1160 2 : poDS->AddSubDataset(osFileName.c_str(), *ptr);
1161 2 : ptr++;
1162 : }
1163 1 : CSLDestroy(papszFileNames);
1164 1 : return poDS;
1165 : }
1166 : }
1167 : }
1168 :
1169 2182 : if (!osGENFileName.empty() && !osIMGFileName.empty())
1170 : {
1171 8 : if (poOpenInfo->eAccess == GA_Update)
1172 : {
1173 0 : ReportUpdateNotSupportedByDriver("ADRG");
1174 8 : return nullptr;
1175 : }
1176 :
1177 8 : DDFModule module;
1178 8 : DDFRecord *record = nullptr;
1179 8 : if (nRecordIndex >= 0 && module.Open(osGENFileName.c_str(), TRUE))
1180 : {
1181 24 : for (int i = 0; i <= nRecordIndex; i++)
1182 : {
1183 18 : CPLPushErrorHandler(CPLQuietErrorHandler);
1184 18 : record = module.ReadRecord();
1185 18 : CPLPopErrorHandler();
1186 18 : CPLErrorReset();
1187 18 : if (record == nullptr)
1188 0 : break;
1189 : }
1190 : }
1191 :
1192 : ADRGDataset *poDS =
1193 8 : OpenDataset(osGENFileName.c_str(), osIMGFileName.c_str(), record);
1194 :
1195 8 : if (poDS)
1196 : {
1197 : /* -------------------------------------------------------------- */
1198 : /* Initialize any PAM information. */
1199 : /* -------------------------------------------------------------- */
1200 8 : poDS->SetDescription(poOpenInfo->pszFilename);
1201 8 : poDS->TryLoadXML();
1202 :
1203 : /* -------------------------------------------------------------- */
1204 : /* Check for external overviews. */
1205 : /* -------------------------------------------------------------- */
1206 8 : if (bFromSubdataset)
1207 2 : poDS->oOvManager.Initialize(poDS, osIMGFileName.c_str());
1208 : else
1209 6 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
1210 :
1211 8 : return poDS;
1212 : }
1213 : }
1214 :
1215 2174 : return nullptr;
1216 : }
1217 :
1218 : /************************************************************************/
1219 : /* GDALRegister_ADRG() */
1220 : /************************************************************************/
1221 :
1222 2038 : void GDALRegister_ADRG()
1223 :
1224 : {
1225 2038 : if (GDALGetDriverByName("ADRG") != nullptr)
1226 283 : return;
1227 :
1228 1755 : GDALDriver *poDriver = new GDALDriver();
1229 :
1230 1755 : poDriver->SetDescription("ADRG");
1231 1755 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1232 1755 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1233 1755 : "ARC Digitized Raster Graphics");
1234 1755 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/adrg.html");
1235 1755 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gen");
1236 1755 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
1237 1755 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1238 :
1239 1755 : poDriver->pfnOpen = ADRGDataset::Open;
1240 :
1241 1755 : GetGDALDriverManager()->RegisterDriver(poDriver);
1242 : }
|