Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: RPF TOC read Translator
4 : * Purpose: Implementation of RPFTOCDataset and RPFTOCSubDataset.
5 : * Author: Even Rouault, even.rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "rpftoclib.h"
15 :
16 : #include <array>
17 : #include <cmath>
18 : #include <cstdio>
19 : #include <cstring>
20 :
21 : #include "cpl_conv.h"
22 : #include "cpl_error.h"
23 : #include "cpl_multiproc.h"
24 : #include "cpl_string.h"
25 : #include "cpl_vsi.h"
26 : #include "gdal.h"
27 : #include "gdal_frmts.h"
28 : #include "gdal_pam.h"
29 : #include "gdal_priv.h"
30 : #include "gdal_proxy.h"
31 : #include "ogr_spatialref.h"
32 : #include "nitflib.h"
33 : #include "vrtdataset.h"
34 : #include "nitfdrivercore.h"
35 :
36 : constexpr int GEOTRSFRM_TOPLEFT_X = 0;
37 : constexpr int GEOTRSFRM_WE_RES = 1;
38 : constexpr int GEOTRSFRM_ROTATION_PARAM1 = 2;
39 : constexpr int GEOTRSFRM_TOPLEFT_Y = 3;
40 : constexpr int GEOTRSFRM_ROTATION_PARAM2 = 4;
41 : constexpr int GEOTRSFRM_NS_RES = 5;
42 :
43 : /** Overview of used classes :
44 : - RPFTOCDataset : lists the different subdatasets, listed in the A.TOC,
45 : as subdatasets
46 : - RPFTOCSubDataset : one of these subdatasets, implemented as a VRT, of
47 : the relevant NITF tiles
48 : - RPFTOCProxyRasterDataSet : a "proxy" dataset that maps to a NITF tile
49 : - RPFTOCProxyRasterBandPalette / RPFTOCProxyRasterBandRGBA : bands of
50 : RPFTOCProxyRasterDataSet
51 : */
52 :
53 : /************************************************************************/
54 : /* ==================================================================== */
55 : /* RPFTOCDataset */
56 : /* ==================================================================== */
57 : /************************************************************************/
58 :
59 : class RPFTOCDataset final : public GDALPamDataset
60 : {
61 : char **papszSubDatasets = nullptr;
62 : OGRSpatialReference m_oSRS{};
63 : int bGotGeoTransform = false;
64 : std::array<double, 6> adfGeoTransform = {0};
65 : char **papszFileList = nullptr;
66 : CPL_DISALLOW_COPY_ASSIGN(RPFTOCDataset)
67 :
68 : public:
69 3 : RPFTOCDataset()
70 3 : {
71 3 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
72 3 : }
73 :
74 6 : virtual ~RPFTOCDataset()
75 3 : {
76 3 : CSLDestroy(papszSubDatasets);
77 3 : CSLDestroy(papszFileList);
78 6 : }
79 :
80 : virtual char **GetMetadata(const char *pszDomain = "") override;
81 :
82 0 : virtual char **GetFileList() override
83 : {
84 0 : return CSLDuplicate(papszFileList);
85 : }
86 :
87 : void AddSubDataset(const char *pszFilename, RPFTocEntry *tocEntry);
88 :
89 3 : void SetSize(int rasterXSize, int rasterYSize)
90 : {
91 3 : nRasterXSize = rasterXSize;
92 3 : nRasterYSize = rasterYSize;
93 3 : }
94 :
95 0 : virtual CPLErr GetGeoTransform(double *padfGeoTransform) override
96 : {
97 0 : if (bGotGeoTransform)
98 : {
99 0 : memcpy(padfGeoTransform, adfGeoTransform.data(),
100 : sizeof(adfGeoTransform));
101 0 : return CE_None;
102 : }
103 0 : return CE_Failure;
104 : }
105 :
106 3 : virtual CPLErr SetGeoTransform(double *padfGeoTransform) override
107 : {
108 3 : bGotGeoTransform = TRUE;
109 3 : memcpy(adfGeoTransform.data(), padfGeoTransform,
110 : sizeof(adfGeoTransform));
111 3 : return CE_None;
112 : }
113 :
114 0 : const OGRSpatialReference *GetSpatialRef() const override
115 : {
116 0 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
117 : }
118 :
119 3 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override
120 : {
121 3 : m_oSRS.Clear();
122 3 : if (poSRS)
123 3 : m_oSRS = *poSRS;
124 3 : return CE_None;
125 : }
126 :
127 : static int IsNITFFileTOC(NITFFile *psFile);
128 : static GDALDataset *OpenFileTOC(NITFFile *psFile, const char *pszFilename,
129 : const char *entryName,
130 : const char *openInformationName);
131 :
132 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
133 : };
134 :
135 : /************************************************************************/
136 : /* ==================================================================== */
137 : /* RPFTOCSubDataset */
138 : /* ==================================================================== */
139 : /************************************************************************/
140 :
141 : class RPFTOCSubDataset final : public VRTDataset
142 : {
143 : int cachedTileBlockXOff = -1;
144 : int cachedTileBlockYOff = -1;
145 : void *cachedTileData = nullptr;
146 : int cachedTileDataSize = 0;
147 : const char *cachedTileFileName = nullptr;
148 : char **papszFileList = nullptr;
149 : CPL_DISALLOW_COPY_ASSIGN(RPFTOCSubDataset)
150 :
151 : public:
152 10 : RPFTOCSubDataset(int nXSize, int nYSize) : VRTDataset(nXSize, nYSize)
153 : {
154 : /* Don't try to write a VRT file */
155 10 : SetWritable(FALSE);
156 :
157 : /* The driver is set to VRT in VRTDataset constructor. */
158 : /* We have to set it to the expected value ! */
159 10 : poDriver = GDALDriver::FromHandle(GDALGetDriverByName("RPFTOC"));
160 10 : }
161 :
162 : ~RPFTOCSubDataset() override;
163 :
164 5 : virtual char **GetFileList() override
165 : {
166 5 : return CSLDuplicate(papszFileList);
167 : }
168 :
169 288 : void *GetCachedTile(const char *tileFileName, int nBlockXOff,
170 : int nBlockYOff)
171 : {
172 288 : if (cachedTileFileName == tileFileName &&
173 0 : cachedTileBlockXOff == nBlockXOff &&
174 0 : cachedTileBlockYOff == nBlockYOff)
175 : {
176 0 : return cachedTileData;
177 : }
178 :
179 288 : return nullptr;
180 : }
181 :
182 288 : void SetCachedTile(const char *tileFileName, int nBlockXOff, int nBlockYOff,
183 : const void *pData, int dataSize)
184 : {
185 288 : if (cachedTileData == nullptr || dataSize > cachedTileDataSize)
186 : {
187 2 : cachedTileData = CPLRealloc(cachedTileData, dataSize);
188 2 : cachedTileDataSize = dataSize;
189 : }
190 288 : memcpy(cachedTileData, pData, dataSize);
191 288 : cachedTileFileName = tileFileName;
192 288 : cachedTileBlockXOff = nBlockXOff;
193 288 : cachedTileBlockYOff = nBlockYOff;
194 288 : }
195 :
196 : static GDALDataset *CreateDataSetFromTocEntry(
197 : const char *openInformationName, const char *pszTOCFileName, int nEntry,
198 : const RPFTocEntry *entry, int isRGBA, char **papszMetadataRPFTOCFile);
199 : };
200 :
201 20 : RPFTOCSubDataset::~RPFTOCSubDataset()
202 : {
203 10 : CSLDestroy(papszFileList);
204 10 : CPLFree(cachedTileData);
205 20 : }
206 :
207 : /************************************************************************/
208 : /* ==================================================================== */
209 : /* RPFTOCProxyRasterDataSet */
210 : /* ==================================================================== */
211 : /************************************************************************/
212 :
213 : class RPFTOCProxyRasterDataSet final : public GDALProxyPoolDataset
214 : {
215 : /* The following parameters are only for sanity checking */
216 : bool checkDone = false;
217 : bool checkOK = false;
218 : const double nwLong;
219 : const double nwLat;
220 : GDALColorTable *colorTableRef = nullptr;
221 : int bHasNoDataValue = false;
222 : double noDataValue = 0;
223 : RPFTOCSubDataset *const subdataset;
224 :
225 : CPL_DISALLOW_COPY_ASSIGN(RPFTOCProxyRasterDataSet)
226 :
227 : public:
228 : RPFTOCProxyRasterDataSet(RPFTOCSubDataset *subdataset, const char *fileName,
229 : int nRasterXSize, int nRasterYSize,
230 : int nBlockXSize, int nBlockYSize,
231 : const char *projectionRef, double nwLong,
232 : double nwLat, int nBands);
233 :
234 7 : void SetNoDataValue(double noDataValueIn)
235 : {
236 7 : this->noDataValue = noDataValueIn;
237 7 : bHasNoDataValue = TRUE;
238 7 : }
239 :
240 3 : double GetNoDataValue(int *pbHasNoDataValue)
241 : {
242 3 : if (pbHasNoDataValue)
243 3 : *pbHasNoDataValue = this->bHasNoDataValue;
244 3 : return noDataValue;
245 : }
246 :
247 : GDALDataset *RefUnderlyingDataset() const override;
248 :
249 400 : void UnrefUnderlyingDataset(GDALDataset *poUnderlyingDataset) const override
250 : {
251 400 : GDALProxyPoolDataset::UnrefUnderlyingDataset(poUnderlyingDataset);
252 400 : }
253 :
254 7 : void SetReferenceColorTable(GDALColorTable *colorTableRefIn)
255 : {
256 7 : this->colorTableRef = colorTableRefIn;
257 7 : }
258 :
259 3 : const GDALColorTable *GetReferenceColorTable() const
260 : {
261 3 : return colorTableRef;
262 : }
263 :
264 : int SanityCheckOK(GDALDataset *sourceDS);
265 :
266 576 : RPFTOCSubDataset *GetSubDataset()
267 : {
268 576 : return subdataset;
269 : }
270 : };
271 :
272 400 : GDALDataset *RPFTOCProxyRasterDataSet::RefUnderlyingDataset() const
273 : {
274 400 : return GDALProxyPoolDataset::RefUnderlyingDataset();
275 : }
276 :
277 : /************************************************************************/
278 : /* ==================================================================== */
279 : /* RPFTOCProxyRasterBandRGBA */
280 : /* ==================================================================== */
281 : /************************************************************************/
282 :
283 : class RPFTOCProxyRasterBandRGBA final : public GDALPamRasterBand
284 : {
285 : bool initDone = false;
286 : std::array<unsigned char, 256> colorTable = {0};
287 : int blockByteSize = 0;
288 :
289 : private:
290 : void Expand(void *pImage, const void *srcImage);
291 :
292 : public:
293 12 : RPFTOCProxyRasterBandRGBA(GDALProxyPoolDataset *poDSIn, int nBandIn,
294 : int nBlockXSizeIn, int nBlockYSizeIn)
295 12 : {
296 12 : this->poDS = poDSIn;
297 12 : nRasterXSize = poDSIn->GetRasterXSize();
298 12 : nRasterYSize = poDSIn->GetRasterYSize();
299 12 : this->nBlockXSize = nBlockXSizeIn;
300 12 : this->nBlockYSize = nBlockYSizeIn;
301 12 : eDataType = GDT_Byte;
302 12 : this->nBand = nBandIn;
303 12 : blockByteSize = nBlockXSize * nBlockYSize;
304 12 : }
305 :
306 0 : virtual GDALColorInterp GetColorInterpretation() override
307 : {
308 0 : return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
309 : }
310 :
311 : protected:
312 : virtual CPLErr IReadBlock(int nBlockXOff, int nBlockYOff,
313 : void *pImage) override;
314 : };
315 :
316 : /************************************************************************/
317 : /* Expand() */
318 : /************************************************************************/
319 :
320 : /* Expand the array or indexed colors to an array of their corresponding R,G,B
321 : * or A component */
322 288 : void RPFTOCProxyRasterBandRGBA::Expand(void *pImage, const void *srcImage)
323 : {
324 : // pImage might be equal to srcImage
325 :
326 288 : if ((blockByteSize & (~3)) != 0)
327 : {
328 18874700 : for (int i = 0; i < blockByteSize; i++)
329 : {
330 18874400 : static_cast<unsigned char *>(pImage)[i] =
331 18874400 : colorTable[static_cast<const unsigned char *>(srcImage)[i]];
332 : }
333 : }
334 : else
335 : {
336 0 : const int nIter = blockByteSize / 4;
337 0 : for (int i = 0; i < nIter; i++)
338 : {
339 0 : const unsigned int four_pixels =
340 0 : static_cast<const unsigned int *>(srcImage)[i];
341 0 : static_cast<unsigned int *>(pImage)[i] =
342 0 : (colorTable[four_pixels >> 24] << 24) |
343 0 : (colorTable[(four_pixels >> 16) & 0xFF] << 16) |
344 0 : (colorTable[(four_pixels >> 8) & 0xFF] << 8) |
345 0 : colorTable[four_pixels & 0xFF];
346 : }
347 : }
348 288 : }
349 :
350 : /************************************************************************/
351 : /* IReadBlock() */
352 : /************************************************************************/
353 :
354 288 : CPLErr RPFTOCProxyRasterBandRGBA::IReadBlock(int nBlockXOff, int nBlockYOff,
355 : void *pImage)
356 : {
357 : CPLErr ret;
358 288 : RPFTOCProxyRasterDataSet *proxyDS =
359 : reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS);
360 :
361 288 : GDALDataset *ds = proxyDS->RefUnderlyingDataset();
362 288 : if (ds)
363 : {
364 288 : if (proxyDS->SanityCheckOK(ds) == FALSE)
365 : {
366 0 : proxyDS->UnrefUnderlyingDataset(ds);
367 0 : return CE_Failure;
368 : }
369 :
370 288 : GDALRasterBand *srcBand = ds->GetRasterBand(1);
371 288 : if (initDone == FALSE)
372 : {
373 8 : GDALColorTable *srcColorTable = srcBand->GetColorTable();
374 : int bHasNoDataValue;
375 : int noDataValue =
376 8 : static_cast<int>(srcBand->GetNoDataValue(&bHasNoDataValue));
377 8 : const int nEntries = srcColorTable->GetColorEntryCount();
378 1744 : for (int i = 0; i < nEntries; i++)
379 : {
380 1736 : const GDALColorEntry *entry = srcColorTable->GetColorEntry(i);
381 1736 : if (nBand == 1)
382 434 : colorTable[i] = static_cast<unsigned char>(entry->c1);
383 1302 : else if (nBand == 2)
384 434 : colorTable[i] = static_cast<unsigned char>(entry->c2);
385 868 : else if (nBand == 3)
386 434 : colorTable[i] = static_cast<unsigned char>(entry->c3);
387 : else
388 : {
389 866 : colorTable[i] = (bHasNoDataValue && i == noDataValue)
390 : ? 0
391 432 : : static_cast<unsigned char>(entry->c4);
392 : }
393 : }
394 8 : if (bHasNoDataValue && nEntries == noDataValue)
395 0 : colorTable[nEntries] = 0;
396 8 : initDone = TRUE;
397 : }
398 :
399 : /* We use a 1-tile cache as the same source tile will be consecutively
400 : * asked for */
401 : /* computing the R tile, the G tile, the B tile and the A tile */
402 576 : void *cachedImage = proxyDS->GetSubDataset()->GetCachedTile(
403 288 : GetDescription(), nBlockXOff, nBlockYOff);
404 288 : if (cachedImage == nullptr)
405 : {
406 288 : CPLDebug("RPFTOC", "Read (%d, %d) of band %d, of file %s",
407 288 : nBlockXOff, nBlockYOff, nBand, GetDescription());
408 288 : ret = srcBand->ReadBlock(nBlockXOff, nBlockYOff, pImage);
409 288 : if (ret == CE_None)
410 : {
411 288 : proxyDS->GetSubDataset()->SetCachedTile(GetDescription(),
412 : nBlockXOff, nBlockYOff,
413 : pImage, blockByteSize);
414 288 : Expand(pImage, pImage);
415 : }
416 :
417 : /* -------------------------------------------------------------- */
418 : /* Forcibly load the other bands associated with this scanline. */
419 : /* -------------------------------------------------------------- */
420 288 : if (nBand == 1)
421 : {
422 : GDALRasterBlock *poBlock =
423 72 : poDS->GetRasterBand(2)->GetLockedBlockRef(nBlockXOff,
424 72 : nBlockYOff);
425 72 : if (poBlock)
426 72 : poBlock->DropLock();
427 :
428 72 : poBlock = poDS->GetRasterBand(3)->GetLockedBlockRef(nBlockXOff,
429 72 : nBlockYOff);
430 72 : if (poBlock)
431 72 : poBlock->DropLock();
432 :
433 72 : poBlock = poDS->GetRasterBand(4)->GetLockedBlockRef(nBlockXOff,
434 72 : nBlockYOff);
435 72 : if (poBlock)
436 72 : poBlock->DropLock();
437 : }
438 : }
439 : else
440 : {
441 0 : Expand(pImage, cachedImage);
442 0 : ret = CE_None;
443 : }
444 : }
445 : else
446 : {
447 0 : ret = CE_Failure;
448 : }
449 :
450 288 : proxyDS->UnrefUnderlyingDataset(ds);
451 :
452 288 : return ret;
453 : }
454 :
455 : /************************************************************************/
456 : /* ==================================================================== */
457 : /* RPFTOCProxyRasterBandPalette */
458 : /* ==================================================================== */
459 : /************************************************************************/
460 :
461 : class RPFTOCProxyRasterBandPalette final : public GDALPamRasterBand
462 : {
463 : int initDone;
464 : int blockByteSize;
465 : int samePalette;
466 : unsigned char remapLUT[256];
467 :
468 : public:
469 7 : RPFTOCProxyRasterBandPalette(GDALProxyPoolDataset *poDSIn, int nBandIn,
470 : int nBlockXSizeIn, int nBlockYSizeIn)
471 14 : : initDone(FALSE), blockByteSize(nBlockXSizeIn * nBlockYSizeIn),
472 7 : samePalette(0)
473 : {
474 7 : this->poDS = poDSIn;
475 7 : nRasterXSize = poDSIn->GetRasterXSize();
476 7 : nRasterYSize = poDSIn->GetRasterYSize();
477 7 : this->nBlockXSize = nBlockXSizeIn;
478 7 : this->nBlockYSize = nBlockYSizeIn;
479 7 : eDataType = GDT_Byte;
480 7 : this->nBand = nBandIn;
481 7 : memset(remapLUT, 0, sizeof(remapLUT));
482 7 : }
483 :
484 3 : virtual GDALColorInterp GetColorInterpretation() override
485 : {
486 3 : return GCI_PaletteIndex;
487 : }
488 :
489 3 : virtual double GetNoDataValue(int *bHasNoDataValue) override
490 : {
491 3 : return (reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS))
492 3 : ->GetNoDataValue(bHasNoDataValue);
493 : }
494 :
495 3 : virtual GDALColorTable *GetColorTable() override
496 : {
497 : // TODO: This casting is a bit scary.
498 : return const_cast<GDALColorTable *>(
499 3 : reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS)
500 3 : ->GetReferenceColorTable());
501 : }
502 :
503 : protected:
504 : virtual CPLErr IReadBlock(int nBlockXOff, int nBlockYOff,
505 : void *pImage) override;
506 : };
507 :
508 : /************************************************************************/
509 : /* IReadBlock() */
510 : /************************************************************************/
511 :
512 108 : CPLErr RPFTOCProxyRasterBandPalette::IReadBlock(int nBlockXOff, int nBlockYOff,
513 : void *pImage)
514 : {
515 : CPLErr ret;
516 108 : RPFTOCProxyRasterDataSet *proxyDS =
517 : reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS);
518 108 : GDALDataset *ds = proxyDS->RefUnderlyingDataset();
519 108 : if (ds)
520 : {
521 108 : if (proxyDS->SanityCheckOK(ds) == FALSE)
522 : {
523 0 : proxyDS->UnrefUnderlyingDataset(ds);
524 0 : return CE_Failure;
525 : }
526 :
527 108 : GDALRasterBand *srcBand = ds->GetRasterBand(1);
528 108 : ret = srcBand->ReadBlock(nBlockXOff, nBlockYOff, pImage);
529 :
530 108 : if (initDone == FALSE)
531 : {
532 : int approximateMatching;
533 3 : if (srcBand->GetIndexColorTranslationTo(this, remapLUT,
534 3 : &approximateMatching))
535 : {
536 0 : samePalette = FALSE;
537 0 : if (approximateMatching)
538 : {
539 0 : CPLError(
540 : CE_Failure, CPLE_AppDefined,
541 : "Palette for %s is different from reference palette. "
542 : "Coudln't remap exactly all colors. Trying to find "
543 : "closest matches.\n",
544 0 : GetDescription());
545 : }
546 : }
547 : else
548 : {
549 3 : samePalette = TRUE;
550 : }
551 3 : initDone = TRUE;
552 : }
553 :
554 108 : if (samePalette == FALSE)
555 : {
556 0 : unsigned char *data = static_cast<unsigned char *>(pImage);
557 0 : for (int i = 0; i < blockByteSize; i++)
558 : {
559 0 : data[i] = remapLUT[data[i]];
560 : }
561 : }
562 : }
563 : else
564 : {
565 0 : ret = CE_Failure;
566 : }
567 :
568 108 : proxyDS->UnrefUnderlyingDataset(ds);
569 :
570 108 : return ret;
571 : }
572 :
573 : /************************************************************************/
574 : /* RPFTOCProxyRasterDataSet() */
575 : /************************************************************************/
576 :
577 10 : RPFTOCProxyRasterDataSet::RPFTOCProxyRasterDataSet(
578 : RPFTOCSubDataset *subdatasetIn, const char *fileNameIn, int nRasterXSizeIn,
579 : int nRasterYSizeIn, int nBlockXSizeIn, int nBlockYSizeIn,
580 10 : const char *projectionRefIn, double nwLongIn, double nwLatIn, int nBandsIn)
581 : : // Mark as shared since the VRT will take several references if we are in
582 : // RGBA mode (4 bands for this dataset).
583 : GDALProxyPoolDataset(fileNameIn, nRasterXSizeIn, nRasterYSizeIn,
584 : GA_ReadOnly, TRUE, projectionRefIn),
585 10 : nwLong(nwLongIn), nwLat(nwLatIn), subdataset(subdatasetIn)
586 : {
587 10 : if (nBandsIn == 4)
588 : {
589 15 : for (int i = 0; i < 4; i++)
590 : {
591 12 : SetBand(i + 1, new RPFTOCProxyRasterBandRGBA(
592 12 : this, i + 1, nBlockXSizeIn, nBlockYSizeIn));
593 : }
594 : }
595 : else
596 : {
597 7 : SetBand(1, new RPFTOCProxyRasterBandPalette(this, 1, nBlockXSizeIn,
598 7 : nBlockYSizeIn));
599 : }
600 10 : }
601 :
602 : /************************************************************************/
603 : /* SanityCheckOK() */
604 : /************************************************************************/
605 :
606 : #define WARN_ON_FAIL(x) \
607 : do \
608 : { \
609 : if (!(x)) \
610 : { \
611 : CPLError(CE_Warning, CPLE_AppDefined, \
612 : "For %s, assert '" #x "' failed", GetDescription()); \
613 : } \
614 : } while (false)
615 : #define ERROR_ON_FAIL(x) \
616 : do \
617 : { \
618 : if (!(x)) \
619 : { \
620 : CPLError(CE_Warning, CPLE_AppDefined, \
621 : "For %s, assert '" #x "' failed", GetDescription()); \
622 : checkOK = FALSE; \
623 : } \
624 : } while (false)
625 :
626 396 : int RPFTOCProxyRasterDataSet::SanityCheckOK(GDALDataset *sourceDS)
627 : {
628 396 : if (checkDone)
629 391 : return checkOK;
630 :
631 : int src_nBlockXSize;
632 : int src_nBlockYSize;
633 : int nBlockXSize;
634 : int nBlockYSize;
635 5 : double l_adfGeoTransform[6] = {};
636 :
637 5 : checkOK = TRUE;
638 5 : checkDone = TRUE;
639 :
640 5 : sourceDS->GetGeoTransform(l_adfGeoTransform);
641 5 : WARN_ON_FAIL(fabs(l_adfGeoTransform[GEOTRSFRM_TOPLEFT_X] - nwLong) <
642 : l_adfGeoTransform[1]);
643 5 : WARN_ON_FAIL(fabs(l_adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] - nwLat) <
644 : fabs(l_adfGeoTransform[5]));
645 5 : WARN_ON_FAIL(l_adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] == 0 &&
646 : l_adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] ==
647 : 0); /* No rotation */
648 5 : ERROR_ON_FAIL(sourceDS->GetRasterCount() == 1); /* Just 1 band */
649 5 : ERROR_ON_FAIL(sourceDS->GetRasterXSize() == nRasterXSize);
650 5 : ERROR_ON_FAIL(sourceDS->GetRasterYSize() == nRasterYSize);
651 5 : WARN_ON_FAIL(EQUAL(sourceDS->GetProjectionRef(), GetProjectionRef()));
652 5 : sourceDS->GetRasterBand(1)->GetBlockSize(&src_nBlockXSize,
653 : &src_nBlockYSize);
654 5 : GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
655 5 : ERROR_ON_FAIL(src_nBlockXSize == nBlockXSize);
656 5 : ERROR_ON_FAIL(src_nBlockYSize == nBlockYSize);
657 5 : WARN_ON_FAIL(sourceDS->GetRasterBand(1)->GetColorInterpretation() ==
658 : GCI_PaletteIndex);
659 5 : WARN_ON_FAIL(sourceDS->GetRasterBand(1)->GetRasterDataType() == GDT_Byte);
660 :
661 5 : return checkOK;
662 : }
663 :
664 : /************************************************************************/
665 : /* MakeTOCEntryName() */
666 : /************************************************************************/
667 :
668 10 : static const char *MakeTOCEntryName(RPFTocEntry *tocEntry)
669 : {
670 10 : char *str = nullptr;
671 10 : if (tocEntry->seriesAbbreviation)
672 10 : str = const_cast<char *>(CPLSPrintf(
673 10 : "%s_%s_%s_%s_%d", tocEntry->type, tocEntry->seriesAbbreviation,
674 10 : tocEntry->scale, tocEntry->zone, tocEntry->boundaryId));
675 : else
676 0 : str = const_cast<char *>(CPLSPrintf("%s_%s_%s_%d", tocEntry->type,
677 0 : tocEntry->scale, tocEntry->zone,
678 : tocEntry->boundaryId));
679 10 : char *c = str;
680 240 : while (*c)
681 : {
682 230 : if (*c == ':' || *c == ' ')
683 0 : *c = '_';
684 230 : c++;
685 : }
686 10 : return str;
687 : }
688 :
689 : /************************************************************************/
690 : /* AddSubDataset() */
691 : /************************************************************************/
692 :
693 3 : void RPFTOCDataset::AddSubDataset(const char *pszFilename,
694 : RPFTocEntry *tocEntry)
695 :
696 : {
697 : char szName[80];
698 3 : const int nCount = CSLCount(papszSubDatasets) / 2;
699 :
700 3 : snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
701 3 : papszSubDatasets =
702 3 : CSLSetNameValue(papszSubDatasets, szName,
703 : CPLSPrintf("NITF_TOC_ENTRY:%s:%s",
704 : MakeTOCEntryName(tocEntry), pszFilename));
705 :
706 3 : snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
707 3 : if (tocEntry->seriesName && tocEntry->seriesAbbreviation)
708 3 : papszSubDatasets = CSLSetNameValue(
709 : papszSubDatasets, szName,
710 3 : CPLSPrintf("%s:%s:%s:%s:%s:%d", tocEntry->type,
711 : tocEntry->seriesAbbreviation, tocEntry->seriesName,
712 3 : tocEntry->scale, tocEntry->zone, tocEntry->boundaryId));
713 : else
714 0 : papszSubDatasets = CSLSetNameValue(
715 : papszSubDatasets, szName,
716 0 : CPLSPrintf("%s:%s:%s:%d", tocEntry->type, tocEntry->scale,
717 0 : tocEntry->zone, tocEntry->boundaryId));
718 3 : }
719 :
720 : /************************************************************************/
721 : /* GetMetadata() */
722 : /************************************************************************/
723 :
724 3 : char **RPFTOCDataset::GetMetadata(const char *pszDomain)
725 :
726 : {
727 3 : if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
728 3 : return papszSubDatasets;
729 :
730 0 : return GDALPamDataset::GetMetadata(pszDomain);
731 : }
732 :
733 : /************************************************************************/
734 : /* NITFCreateVRTDataSetFromTocEntry() */
735 : /************************************************************************/
736 :
737 : #define ASSERT_CREATE_VRT(x) \
738 : do \
739 : { \
740 : if (!(x)) \
741 : { \
742 : CPLError(CE_Failure, CPLE_AppDefined, \
743 : "For %s, assert '" #x "' failed", \
744 : entry->frameEntries[i].fullFilePath); \
745 : if (poSrcDS) \
746 : GDALClose(poSrcDS); \
747 : CPLFree(projectionRef); \
748 : return nullptr; \
749 : } \
750 : } while (false)
751 :
752 : /* Builds a RPFTOCSubDataset from the set of files of the toc entry */
753 10 : GDALDataset *RPFTOCSubDataset::CreateDataSetFromTocEntry(
754 : const char *openInformationName, const char *pszTOCFileName, int nEntry,
755 : const RPFTocEntry *entry, int isRGBA, char **papszMetadataRPFTOCFile)
756 : {
757 10 : GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("VRT");
758 10 : if (poDriver == nullptr)
759 0 : return nullptr;
760 :
761 10 : const int N = entry->nVertFrames * entry->nHorizFrames;
762 :
763 : /* This may not be reliable. See below */
764 10 : int sizeX =
765 10 : static_cast<int>((entry->seLong - entry->nwLong) /
766 10 : (entry->nHorizFrames * entry->horizInterval) +
767 : 0.5);
768 :
769 10 : int sizeY =
770 10 : static_cast<int>((entry->nwLat - entry->seLat) /
771 10 : (entry->nVertFrames * entry->vertInterval) +
772 : 0.5);
773 :
774 10 : if ((EQUAL(entry->type, "CADRG") || (EQUAL(entry->type, "CIB"))))
775 : {
776 : // for CADRG and CIB the frame size is defined with 1536x1536
777 : // CADRG: see MIL-C-89038: 3.5.2 a - Each frame shall comprise a
778 : // rectangular array of 1536 by 1536 pixels CIB: see MIL-C-89041: 3.5.2
779 : // a - Each frame shall comprise a rectangular array of 1536 by 1536
780 : // pixels
781 10 : sizeX = 1536;
782 10 : sizeY = 1536;
783 : }
784 :
785 10 : int nBlockXSize = 0;
786 10 : int nBlockYSize = 0;
787 10 : double geoTransf[6] = {};
788 10 : char *projectionRef = nullptr;
789 10 : int index = 0;
790 :
791 20 : for (int i = 0; i < N; i++)
792 : {
793 10 : if (!entry->frameEntries[i].fileExists)
794 0 : continue;
795 :
796 10 : if (index == 0)
797 : {
798 : /* Open the first available file to get its geotransform, projection
799 : * ref and block size */
800 : /* Do a few sanity checks too */
801 : /* Ideally we should make these sanity checks now on ALL files, but
802 : * it would be too slow */
803 : /* for large datasets. So these sanity checks will be done at the
804 : * time we really need */
805 : /* to access the file (see SanityCheckOK method) */
806 10 : GDALDataset *poSrcDS = GDALDataset::FromHandle(GDALOpenShared(
807 10 : entry->frameEntries[i].fullFilePath, GA_ReadOnly));
808 10 : ASSERT_CREATE_VRT(poSrcDS);
809 10 : poSrcDS->GetGeoTransform(geoTransf);
810 10 : projectionRef = CPLStrdup(poSrcDS->GetProjectionRef());
811 10 : ASSERT_CREATE_VRT(geoTransf[GEOTRSFRM_ROTATION_PARAM1] == 0 &&
812 : geoTransf[GEOTRSFRM_ROTATION_PARAM2] ==
813 : 0); /* No rotation */
814 10 : ASSERT_CREATE_VRT(poSrcDS->GetRasterCount() == 1); /* Just 1 band */
815 :
816 : /* Tolerance of 1%... This is necessary for CADRG_L22/RPF/A.TOC for
817 : * example */
818 10 : ASSERT_CREATE_VRT(
819 : (entry->horizInterval - geoTransf[GEOTRSFRM_WE_RES]) /
820 : entry->horizInterval <
821 : 0.01); /* X interval same as in TOC */
822 10 : ASSERT_CREATE_VRT(
823 : (entry->vertInterval - (-geoTransf[GEOTRSFRM_NS_RES])) /
824 : entry->vertInterval <
825 : 0.01); /* Y interval same as in TOC */
826 :
827 10 : const int ds_sizeX = poSrcDS->GetRasterXSize();
828 10 : const int ds_sizeY = poSrcDS->GetRasterYSize();
829 : /* for polar zone use the sizes from the dataset */
830 10 : if ((entry->zone[0] == '9') || (entry->zone[0] == 'J'))
831 : {
832 0 : sizeX = ds_sizeX;
833 0 : sizeY = ds_sizeY;
834 : }
835 :
836 : /* In the case the east longitude is 180, there's a great chance
837 : * that it is in fact */
838 : /* truncated in the A.TOC. Thus, the only reliable way to find out
839 : * the tile width, is to */
840 : /* read it from the tile dataset itself... */
841 : /* This is the case for the GNCJNCN dataset that has world coverage
842 : */
843 10 : if (entry->seLong == 180.00)
844 0 : sizeX = ds_sizeX;
845 : else
846 10 : ASSERT_CREATE_VRT(sizeX == ds_sizeX);
847 :
848 10 : ASSERT_CREATE_VRT(sizeY == ds_sizeY);
849 10 : poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
850 10 : ASSERT_CREATE_VRT(
851 : poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
852 : GCI_PaletteIndex);
853 10 : ASSERT_CREATE_VRT(poSrcDS->GetRasterBand(1)->GetRasterDataType() ==
854 : GDT_Byte);
855 10 : GDALClose(poSrcDS);
856 : }
857 :
858 10 : index++;
859 : }
860 :
861 10 : if (index == 0)
862 0 : return nullptr;
863 :
864 : /* ------------------------------------ */
865 : /* Create the VRT with the overall size */
866 : /* ------------------------------------ */
867 : RPFTOCSubDataset *poVirtualDS = new RPFTOCSubDataset(
868 10 : sizeX * entry->nHorizFrames, sizeY * entry->nVertFrames);
869 :
870 10 : if (papszMetadataRPFTOCFile)
871 0 : poVirtualDS->SetMetadata(papszMetadataRPFTOCFile);
872 :
873 10 : poVirtualDS->SetProjection(projectionRef);
874 :
875 10 : geoTransf[GEOTRSFRM_TOPLEFT_X] = entry->nwLong;
876 10 : geoTransf[GEOTRSFRM_TOPLEFT_Y] = entry->nwLat;
877 10 : poVirtualDS->SetGeoTransform(geoTransf);
878 :
879 : int nBands;
880 :
881 : /* In most cases, all the files inside a TOC entry share the same */
882 : /* palette and we could use it for the VRT. */
883 : /* In other cases like for CADRG801_France_250K (TOC entry CADRG_250K_2_2),
884 : */
885 : /* the file for Corsica and the file for Sardegna do not share the same
886 : * palette */
887 : /* however they contain the same RGB triplets and are just ordered
888 : * differently */
889 : /* So we can use the same palette */
890 : /* In the unlikely event where palettes would be incompatible, we can use
891 : * the RGBA */
892 : /* option through the config option RPFTOC_FORCE_RGBA */
893 10 : if (isRGBA == FALSE)
894 : {
895 7 : poVirtualDS->AddBand(GDT_Byte, nullptr);
896 7 : GDALRasterBand *poBand = poVirtualDS->GetRasterBand(1);
897 7 : poBand->SetColorInterpretation(GCI_PaletteIndex);
898 7 : nBands = 1;
899 :
900 14 : for (int i = 0; i < N; i++)
901 : {
902 7 : if (!entry->frameEntries[i].fileExists)
903 0 : continue;
904 :
905 7 : bool bAllBlack = true;
906 7 : GDALDataset *poSrcDS = GDALDataset::FromHandle(GDALOpenShared(
907 7 : entry->frameEntries[i].fullFilePath, GA_ReadOnly));
908 7 : if (poSrcDS != nullptr)
909 : {
910 7 : if (poSrcDS->GetRasterCount() == 1)
911 : {
912 : int bHasNoDataValue;
913 : const double noDataValue =
914 7 : poSrcDS->GetRasterBand(1)->GetNoDataValue(
915 7 : &bHasNoDataValue);
916 7 : if (bHasNoDataValue)
917 7 : poBand->SetNoDataValue(noDataValue);
918 :
919 : /* Avoid setting a color table that is all black (which
920 : * might be */
921 : /* the case of the edge tiles of a RPF subdataset) */
922 : GDALColorTable *poCT =
923 7 : poSrcDS->GetRasterBand(1)->GetColorTable();
924 7 : if (poCT != nullptr)
925 : {
926 1526 : for (int iC = 0; iC < poCT->GetColorEntryCount(); iC++)
927 : {
928 3045 : if (bHasNoDataValue &&
929 1519 : iC == static_cast<int>(noDataValue))
930 7 : continue;
931 :
932 : const GDALColorEntry *psColorEntry =
933 1512 : poCT->GetColorEntry(iC);
934 1512 : if (psColorEntry->c1 != 0 ||
935 1512 : psColorEntry->c2 != 0 || psColorEntry->c3 != 0)
936 : {
937 0 : bAllBlack = false;
938 0 : break;
939 : }
940 : }
941 :
942 : /* Assign it temporarily, in the hope of a better match
943 : */
944 : /* afterwards */
945 7 : poBand->SetColorTable(poCT);
946 7 : if (bAllBlack)
947 : {
948 7 : CPLDebug("RPFTOC",
949 : "Skipping %s. Its palette is all black.",
950 7 : poSrcDS->GetDescription());
951 : }
952 : }
953 : }
954 7 : GDALClose(poSrcDS);
955 : }
956 7 : if (!bAllBlack)
957 0 : break;
958 : }
959 : }
960 : else
961 : {
962 15 : for (int i = 0; i < 4; i++)
963 : {
964 12 : poVirtualDS->AddBand(GDT_Byte, nullptr);
965 12 : GDALRasterBand *poBand = poVirtualDS->GetRasterBand(i + 1);
966 12 : poBand->SetColorInterpretation(
967 12 : static_cast<GDALColorInterp>(GCI_RedBand + i));
968 : }
969 3 : nBands = 4;
970 : }
971 :
972 10 : CPLFree(projectionRef);
973 10 : projectionRef = nullptr;
974 :
975 : /* -------------------------------------------------------------------- */
976 : /* Check for overviews. */
977 : /* -------------------------------------------------------------------- */
978 :
979 10 : poVirtualDS->oOvManager.Initialize(
980 20 : poVirtualDS, CPLString().Printf("%s.%d", pszTOCFileName, nEntry + 1));
981 :
982 10 : poVirtualDS->SetDescription(pszTOCFileName);
983 10 : poVirtualDS->papszFileList = poVirtualDS->GDALDataset::GetFileList();
984 10 : poVirtualDS->SetDescription(openInformationName);
985 :
986 10 : int iFile = 0;
987 20 : for (int i = 0; i < N; i++)
988 : {
989 10 : if (!entry->frameEntries[i].fileExists)
990 0 : continue;
991 :
992 10 : poVirtualDS->SetMetadataItem(CPLSPrintf("FILENAME_%d", iFile),
993 10 : entry->frameEntries[i].fullFilePath);
994 20 : poVirtualDS->papszFileList = CSLAddString(
995 10 : poVirtualDS->papszFileList, entry->frameEntries[i].fullFilePath);
996 10 : iFile++;
997 :
998 : /* We create proxy datasets and raster bands */
999 : /* Using real datasets and raster bands is possible in theory */
1000 : /* However for large datasets, a TOC entry can include several hundreds
1001 : * of files */
1002 : /* and we finally reach the limit of maximum file descriptors open at
1003 : * the same time ! */
1004 : /* So the idea is to warp the datasets into a proxy and open the
1005 : * underlying dataset only when it is */
1006 : /* needed (IRasterIO operation). To improve a bit efficiency, we have a
1007 : * cache of opened */
1008 : /* underlying datasets */
1009 : RPFTOCProxyRasterDataSet *ds = new RPFTOCProxyRasterDataSet(
1010 : reinterpret_cast<RPFTOCSubDataset *>(poVirtualDS),
1011 10 : entry->frameEntries[i].fullFilePath, sizeX, sizeY, nBlockXSize,
1012 10 : nBlockYSize, poVirtualDS->GetProjectionRef(),
1013 10 : entry->nwLong +
1014 10 : entry->frameEntries[i].frameCol * entry->horizInterval * sizeX,
1015 10 : entry->nwLat -
1016 10 : entry->frameEntries[i].frameRow * entry->vertInterval * sizeY,
1017 10 : nBands);
1018 :
1019 10 : if (nBands == 1)
1020 : {
1021 7 : GDALRasterBand *poBand = poVirtualDS->GetRasterBand(1);
1022 7 : ds->SetReferenceColorTable(poBand->GetColorTable());
1023 : int bHasNoDataValue;
1024 7 : const double noDataValue = poBand->GetNoDataValue(&bHasNoDataValue);
1025 7 : if (bHasNoDataValue)
1026 7 : ds->SetNoDataValue(noDataValue);
1027 : }
1028 :
1029 29 : for (int j = 0; j < nBands; j++)
1030 : {
1031 : VRTSourcedRasterBand *poBand =
1032 : reinterpret_cast<VRTSourcedRasterBand *>(
1033 19 : poVirtualDS->GetRasterBand(j + 1));
1034 : /* Place the raster band at the right position in the VRT */
1035 19 : poBand->AddSimpleSource(
1036 : ds->GetRasterBand(j + 1), 0, 0, sizeX, sizeY,
1037 19 : entry->frameEntries[i].frameCol * sizeX,
1038 19 : entry->frameEntries[i].frameRow * sizeY, sizeX, sizeY);
1039 : }
1040 :
1041 : /* The RPFTOCProxyRasterDataSet will be destroyed when its last raster
1042 : * band will be */
1043 : /* destroyed */
1044 10 : ds->Dereference();
1045 : }
1046 :
1047 10 : poVirtualDS->SetMetadataItem("NITF_SCALE", entry->scale);
1048 10 : poVirtualDS->SetMetadataItem(
1049 : "NITF_SERIES_ABBREVIATION",
1050 10 : (entry->seriesAbbreviation) ? entry->seriesAbbreviation : "Unknown");
1051 10 : poVirtualDS->SetMetadataItem("NITF_SERIES_NAME", (entry->seriesName)
1052 : ? entry->seriesName
1053 10 : : "Unknown");
1054 :
1055 10 : return poVirtualDS;
1056 : }
1057 :
1058 : /************************************************************************/
1059 : /* IsNITFFileTOC() */
1060 : /************************************************************************/
1061 :
1062 : /* Check whether this NITF file is a TOC file */
1063 0 : int RPFTOCDataset::IsNITFFileTOC(NITFFile *psFile)
1064 : {
1065 : const char *fileTitle =
1066 0 : CSLFetchNameValue(psFile->papszMetadata, "NITF_FTITLE");
1067 0 : while (fileTitle && *fileTitle)
1068 : {
1069 0 : if (EQUAL(fileTitle, "A.TOC"))
1070 : {
1071 0 : return TRUE;
1072 : }
1073 0 : fileTitle++;
1074 : }
1075 0 : return FALSE;
1076 : }
1077 :
1078 : /************************************************************************/
1079 : /* OpenFileTOC() */
1080 : /************************************************************************/
1081 :
1082 : /* Create a dataset from a TOC file */
1083 : /* If psFile == NULL, the TOC file has no NITF header */
1084 : /* If entryName != NULL, the dataset will be made just of the entry of the TOC
1085 : * file */
1086 10 : GDALDataset *RPFTOCDataset::OpenFileTOC(NITFFile *psFile,
1087 : const char *pszFilename,
1088 : const char *entryName,
1089 : const char *openInformationName)
1090 : {
1091 : char buffer[48];
1092 10 : VSILFILE *fp = nullptr;
1093 10 : if (psFile == nullptr)
1094 : {
1095 10 : fp = VSIFOpenL(pszFilename, "rb");
1096 :
1097 10 : if (fp == nullptr)
1098 : {
1099 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s.",
1100 : pszFilename);
1101 0 : return nullptr;
1102 : }
1103 10 : if (VSIFReadL(buffer, 1, 48, fp) != 48)
1104 : {
1105 0 : CPLError(CE_Failure, CPLE_FileIO, "I/O error");
1106 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
1107 0 : return nullptr;
1108 : }
1109 : }
1110 : const int isRGBA =
1111 10 : CPLTestBool(CPLGetConfigOption("RPFTOC_FORCE_RGBA", "NO"));
1112 10 : RPFToc *toc = (psFile) ? RPFTOCRead(pszFilename, psFile)
1113 10 : : RPFTOCReadFromBuffer(pszFilename, fp, buffer);
1114 10 : if (fp)
1115 10 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
1116 10 : fp = nullptr;
1117 :
1118 10 : if (entryName != nullptr)
1119 : {
1120 7 : if (toc)
1121 : {
1122 7 : for (int i = 0; i < toc->nEntries; i++)
1123 : {
1124 7 : if (EQUAL(entryName, MakeTOCEntryName(&toc->entries[i])))
1125 : {
1126 : GDALDataset *ds =
1127 7 : RPFTOCSubDataset::CreateDataSetFromTocEntry(
1128 : openInformationName, pszFilename, i,
1129 7 : &toc->entries[i], isRGBA,
1130 : (psFile) ? psFile->papszMetadata : nullptr);
1131 :
1132 7 : RPFTOCFree(toc);
1133 7 : return ds;
1134 : }
1135 : }
1136 0 : CPLError(CE_Failure, CPLE_AppDefined,
1137 : "The entry %s does not exist in file %s.", entryName,
1138 : pszFilename);
1139 : }
1140 0 : RPFTOCFree(toc);
1141 0 : return nullptr;
1142 : }
1143 :
1144 3 : if (toc)
1145 : {
1146 3 : RPFTOCDataset *ds = new RPFTOCDataset();
1147 3 : if (psFile)
1148 0 : ds->SetMetadata(psFile->papszMetadata);
1149 :
1150 3 : bool ok = false;
1151 3 : char *projectionRef = nullptr;
1152 3 : double nwLong = 0.0;
1153 3 : double nwLat = 0.0;
1154 3 : double seLong = 0.0;
1155 3 : double seLat = 0.0;
1156 3 : double adfGeoTransform[6] = {};
1157 :
1158 3 : ds->papszFileList = CSLAddString(ds->papszFileList, pszFilename);
1159 :
1160 6 : for (int i = 0; i < toc->nEntries; i++)
1161 : {
1162 3 : if (!toc->entries[i].isOverviewOrLegend)
1163 : {
1164 : GDALDataset *tmpDS =
1165 6 : RPFTOCSubDataset::CreateDataSetFromTocEntry(
1166 3 : openInformationName, pszFilename, i, &toc->entries[i],
1167 : isRGBA, nullptr);
1168 3 : if (tmpDS)
1169 : {
1170 3 : char **papszSubDatasetFileList = tmpDS->GetFileList();
1171 : /* Yes, begin at 1, since the first is the a.toc */
1172 6 : ds->papszFileList = CSLInsertStrings(
1173 3 : ds->papszFileList, -1, papszSubDatasetFileList + 1);
1174 3 : CSLDestroy(papszSubDatasetFileList);
1175 :
1176 3 : tmpDS->GetGeoTransform(adfGeoTransform);
1177 3 : if (projectionRef == nullptr)
1178 : {
1179 3 : ok = true;
1180 3 : projectionRef = CPLStrdup(tmpDS->GetProjectionRef());
1181 3 : nwLong = adfGeoTransform[GEOTRSFRM_TOPLEFT_X];
1182 3 : nwLat = adfGeoTransform[GEOTRSFRM_TOPLEFT_Y];
1183 6 : seLong = nwLong + adfGeoTransform[GEOTRSFRM_WE_RES] *
1184 3 : tmpDS->GetRasterXSize();
1185 6 : seLat = nwLat + adfGeoTransform[GEOTRSFRM_NS_RES] *
1186 3 : tmpDS->GetRasterYSize();
1187 : }
1188 0 : else if (ok)
1189 : {
1190 0 : double _nwLong = adfGeoTransform[GEOTRSFRM_TOPLEFT_X];
1191 0 : double _nwLat = adfGeoTransform[GEOTRSFRM_TOPLEFT_Y];
1192 : double _seLong =
1193 0 : _nwLong + adfGeoTransform[GEOTRSFRM_WE_RES] *
1194 0 : tmpDS->GetRasterXSize();
1195 : double _seLat =
1196 0 : _nwLat + adfGeoTransform[GEOTRSFRM_NS_RES] *
1197 0 : tmpDS->GetRasterYSize();
1198 0 : if (!EQUAL(projectionRef, tmpDS->GetProjectionRef()))
1199 0 : ok = false;
1200 0 : if (_nwLong < nwLong)
1201 0 : nwLong = _nwLong;
1202 0 : if (_nwLat > nwLat)
1203 0 : nwLat = _nwLat;
1204 0 : if (_seLong > seLong)
1205 0 : seLong = _seLong;
1206 0 : if (_seLat < seLat)
1207 0 : seLat = _seLat;
1208 : }
1209 3 : delete tmpDS;
1210 3 : ds->AddSubDataset(pszFilename, &toc->entries[i]);
1211 : }
1212 : }
1213 : }
1214 3 : if (ok)
1215 : {
1216 3 : adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = nwLong;
1217 3 : adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = nwLat;
1218 3 : ds->SetSize(
1219 3 : static_cast<int>(0.5 + (seLong - nwLong) /
1220 3 : adfGeoTransform[GEOTRSFRM_WE_RES]),
1221 3 : static_cast<int>(0.5 + (seLat - nwLat) /
1222 3 : adfGeoTransform[GEOTRSFRM_NS_RES]));
1223 :
1224 3 : ds->SetGeoTransform(adfGeoTransform);
1225 3 : ds->SetProjection(projectionRef);
1226 : }
1227 3 : CPLFree(projectionRef);
1228 3 : RPFTOCFree(toc);
1229 :
1230 : /* --------------------------------------------------------------------
1231 : */
1232 : /* Initialize any PAM information. */
1233 : /* --------------------------------------------------------------------
1234 : */
1235 3 : ds->SetDescription(pszFilename);
1236 3 : ds->TryLoadXML();
1237 :
1238 3 : return ds;
1239 : }
1240 :
1241 0 : return nullptr;
1242 : }
1243 :
1244 : /************************************************************************/
1245 : /* Open() */
1246 : /************************************************************************/
1247 :
1248 10 : GDALDataset *RPFTOCDataset::Open(GDALOpenInfo *poOpenInfo)
1249 :
1250 : {
1251 10 : if (!RPFTOCDriverIdentify(poOpenInfo))
1252 0 : return nullptr;
1253 :
1254 10 : const char *pszFilename = poOpenInfo->pszFilename;
1255 10 : char *entryName = nullptr;
1256 :
1257 10 : if (STARTS_WITH_CI(pszFilename, "NITF_TOC_ENTRY:"))
1258 : {
1259 7 : pszFilename += strlen("NITF_TOC_ENTRY:");
1260 7 : entryName = CPLStrdup(pszFilename);
1261 7 : char *c = entryName;
1262 168 : while (*c != '\0' && *c != ':')
1263 161 : c++;
1264 7 : if (*c != ':')
1265 : {
1266 0 : CPLFree(entryName);
1267 0 : return nullptr;
1268 : }
1269 7 : *c = 0;
1270 :
1271 168 : while (*pszFilename != '\0' && *pszFilename != ':')
1272 161 : pszFilename++;
1273 7 : pszFilename++;
1274 : }
1275 :
1276 10 : if (RPFTOCIsNonNITFFileTOC((entryName != nullptr) ? nullptr : poOpenInfo,
1277 10 : pszFilename))
1278 : {
1279 20 : GDALDataset *poDS = OpenFileTOC(nullptr, pszFilename, entryName,
1280 10 : poOpenInfo->pszFilename);
1281 :
1282 10 : CPLFree(entryName);
1283 :
1284 10 : if (poDS && poOpenInfo->eAccess == GA_Update)
1285 : {
1286 0 : ReportUpdateNotSupportedByDriver("RPFTOC");
1287 0 : delete poDS;
1288 0 : return nullptr;
1289 : }
1290 :
1291 10 : return poDS;
1292 : }
1293 :
1294 : /* -------------------------------------------------------------------- */
1295 : /* Open the file with library. */
1296 : /* -------------------------------------------------------------------- */
1297 0 : NITFFile *psFile = NITFOpen(pszFilename, FALSE);
1298 0 : if (psFile == nullptr)
1299 : {
1300 0 : CPLFree(entryName);
1301 0 : return nullptr;
1302 : }
1303 :
1304 : /* -------------------------------------------------------------------- */
1305 : /* Check if it is a TOC file . */
1306 : /* -------------------------------------------------------------------- */
1307 0 : if (IsNITFFileTOC(psFile))
1308 : {
1309 0 : GDALDataset *poDS = OpenFileTOC(psFile, pszFilename, entryName,
1310 0 : poOpenInfo->pszFilename);
1311 0 : NITFClose(psFile);
1312 0 : CPLFree(entryName);
1313 :
1314 0 : if (poDS && poOpenInfo->eAccess == GA_Update)
1315 : {
1316 0 : ReportUpdateNotSupportedByDriver("RPFTOC");
1317 0 : delete poDS;
1318 0 : return nullptr;
1319 : }
1320 :
1321 0 : return poDS;
1322 : }
1323 : else
1324 : {
1325 0 : CPLError(CE_Failure, CPLE_AppDefined, "File %s is not a TOC file.",
1326 : pszFilename);
1327 0 : NITFClose(psFile);
1328 0 : CPLFree(entryName);
1329 0 : return nullptr;
1330 : }
1331 : }
1332 :
1333 : /************************************************************************/
1334 : /* GDALRegister_RPFTOC() */
1335 : /************************************************************************/
1336 :
1337 1911 : void GDALRegister_RPFTOC()
1338 :
1339 : {
1340 1911 : if (GDALGetDriverByName(RPFTOC_DRIVER_NAME) != nullptr)
1341 282 : return;
1342 :
1343 1629 : GDALDriver *poDriver = new GDALDriver();
1344 1629 : RPFTOCDriverSetCommonMetadata(poDriver);
1345 :
1346 1629 : poDriver->pfnOpen = RPFTOCDataset::Open;
1347 :
1348 1629 : GetGDALDriverManager()->RegisterDriver(poDriver);
1349 : }
|