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