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