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