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