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