Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ECRG TOC read Translator
4 : * Purpose: Implementation of ECRGTOCDataset and ECRGTOCSubDataset.
5 : * Author: Even Rouault, even.rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 :
15 : #include <array>
16 : #include <cassert>
17 : #include <cmath>
18 : #include <cstddef>
19 : #include <cstdio>
20 : #include <cstdlib>
21 : #include <cstring>
22 : #include <memory>
23 : #include <string>
24 : #include <vector>
25 :
26 : #include "cpl_conv.h"
27 : #include "cpl_error.h"
28 : #include "cpl_minixml.h"
29 : #include "cpl_string.h"
30 : #include "gdal.h"
31 : #include "gdal_frmts.h"
32 : #include "gdal_pam.h"
33 : #include "gdal_priv.h"
34 : #include "gdal_proxy.h"
35 : #include "ogr_srs_api.h"
36 : #include "vrtdataset.h"
37 : #include "nitfdrivercore.h"
38 :
39 : /** Overview of used classes :
40 : - ECRGTOCDataset : lists the different subdatasets, listed in the .xml,
41 : as subdatasets
42 : - ECRGTOCSubDataset : one of these subdatasets, implemented as a VRT, of
43 : the relevant NITF tiles
44 : */
45 :
46 : namespace
47 : {
48 : typedef struct
49 : {
50 : const char *pszName;
51 : const char *pszPath;
52 : int nScale;
53 : int nZone;
54 : } FrameDesc;
55 : } // namespace
56 :
57 : /************************************************************************/
58 : /* ==================================================================== */
59 : /* ECRGTOCDataset */
60 : /* ==================================================================== */
61 : /************************************************************************/
62 :
63 : class ECRGTOCDataset final : public GDALPamDataset
64 : {
65 : OGRSpatialReference m_oSRS{};
66 : char **papszSubDatasets = nullptr;
67 : GDALGeoTransform m_gt{};
68 : char **papszFileList = nullptr;
69 :
70 : CPL_DISALLOW_COPY_ASSIGN(ECRGTOCDataset)
71 :
72 : public:
73 21 : ECRGTOCDataset()
74 21 : {
75 21 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
76 21 : m_oSRS.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
77 21 : }
78 :
79 42 : virtual ~ECRGTOCDataset()
80 21 : {
81 21 : CSLDestroy(papszSubDatasets);
82 21 : CSLDestroy(papszFileList);
83 42 : }
84 :
85 : virtual char **GetMetadata(const char *pszDomain = "") override;
86 :
87 1 : virtual char **GetFileList() override
88 : {
89 1 : return CSLDuplicate(papszFileList);
90 : }
91 :
92 : void AddSubDataset(const char *pszFilename, const char *pszProductTitle,
93 : const char *pszDiscId, const char *pszScale);
94 :
95 1 : virtual CPLErr GetGeoTransform(GDALGeoTransform >) const override
96 : {
97 1 : gt = m_gt;
98 1 : return CE_None;
99 : }
100 :
101 1 : const OGRSpatialReference *GetSpatialRef() const override
102 : {
103 1 : return &m_oSRS;
104 : }
105 :
106 : static GDALDataset *Build(const char *pszTOCFilename, CPLXMLNode *psXML,
107 : const std::string &osProduct,
108 : const std::string &osDiscId,
109 : const std::string &osScale,
110 : const char *pszFilename);
111 :
112 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
113 : };
114 :
115 : /************************************************************************/
116 : /* ==================================================================== */
117 : /* ECRGTOCSubDataset */
118 : /* ==================================================================== */
119 : /************************************************************************/
120 :
121 : class ECRGTOCSubDataset final : public VRTDataset
122 : {
123 : char **papszFileList = nullptr;
124 : CPL_DISALLOW_COPY_ASSIGN(ECRGTOCSubDataset)
125 :
126 : public:
127 11 : ECRGTOCSubDataset(int nXSize, int nYSize) : VRTDataset(nXSize, nYSize)
128 : {
129 : /* Don't try to write a VRT file */
130 11 : SetWritable(FALSE);
131 :
132 : /* The driver is set to VRT in VRTDataset constructor. */
133 : /* We have to set it to the expected value ! */
134 11 : poDriver = GDALDriver::FromHandle(GDALGetDriverByName("ECRGTOC"));
135 11 : }
136 :
137 : ~ECRGTOCSubDataset() override;
138 :
139 2 : virtual char **GetFileList() override
140 : {
141 2 : return CSLDuplicate(papszFileList);
142 : }
143 :
144 : static GDALDataset *
145 : Build(const char *pszProductTitle, const char *pszDiscId, int nScale,
146 : int nCountSubDataset, const char *pszTOCFilename,
147 : const std::vector<FrameDesc> &aosFrameDesc, double dfGlobalMinX,
148 : double dfGlobalMinY, double dfGlobalMaxX, double dfGlobalMaxY,
149 : double dfGlobalPixelXSize, double dfGlobalPixelYSize);
150 : };
151 :
152 22 : ECRGTOCSubDataset::~ECRGTOCSubDataset()
153 : {
154 11 : CSLDestroy(papszFileList);
155 22 : }
156 :
157 : /************************************************************************/
158 : /* LaunderString() */
159 : /************************************************************************/
160 :
161 67 : static CPLString LaunderString(const char *pszStr)
162 : {
163 67 : CPLString osRet(pszStr);
164 632 : for (size_t i = 0; i < osRet.size(); i++)
165 : {
166 565 : if (osRet[i] == ':' || osRet[i] == ' ')
167 42 : osRet[i] = '_';
168 : }
169 67 : return osRet;
170 : }
171 :
172 : /************************************************************************/
173 : /* AddSubDataset() */
174 : /************************************************************************/
175 :
176 9 : void ECRGTOCDataset::AddSubDataset(const char *pszFilename,
177 : const char *pszProductTitle,
178 : const char *pszDiscId, const char *pszScale)
179 :
180 : {
181 : char szName[80];
182 9 : const int nCount = CSLCount(papszSubDatasets) / 2;
183 :
184 9 : snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
185 27 : papszSubDatasets = CSLSetNameValue(
186 : papszSubDatasets, szName,
187 : CPLSPrintf("ECRG_TOC_ENTRY:%s:%s:%s:%s",
188 18 : LaunderString(pszProductTitle).c_str(),
189 18 : LaunderString(pszDiscId).c_str(),
190 18 : LaunderString(pszScale).c_str(), pszFilename));
191 :
192 9 : snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
193 9 : papszSubDatasets =
194 9 : CSLSetNameValue(papszSubDatasets, szName,
195 : CPLSPrintf("Product %s, disc %s, scale %s",
196 : pszProductTitle, pszDiscId, pszScale));
197 9 : }
198 :
199 : /************************************************************************/
200 : /* GetMetadata() */
201 : /************************************************************************/
202 :
203 7 : char **ECRGTOCDataset::GetMetadata(const char *pszDomain)
204 :
205 : {
206 7 : if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
207 7 : return papszSubDatasets;
208 :
209 0 : return GDALPamDataset::GetMetadata(pszDomain);
210 : }
211 :
212 : /************************************************************************/
213 : /* GetScaleFromString() */
214 : /************************************************************************/
215 :
216 22 : static int GetScaleFromString(const char *pszScale)
217 : {
218 22 : const char *pszPtr = strstr(pszScale, "1:");
219 22 : if (pszPtr)
220 22 : pszPtr = pszPtr + 2;
221 : else
222 0 : pszPtr = pszScale;
223 :
224 22 : int nScale = 0;
225 : char ch;
226 112 : while ((ch = *pszPtr) != '\0')
227 : {
228 112 : if (ch >= '0' && ch <= '9')
229 68 : nScale = nScale * 10 + ch - '0';
230 44 : else if (ch == ' ')
231 : ;
232 22 : else if (ch == 'k' || ch == 'K')
233 22 : return nScale * 1000;
234 0 : else if (ch == 'm' || ch == 'M')
235 0 : return nScale * 1000000;
236 : else
237 0 : return 0;
238 90 : pszPtr++;
239 : }
240 0 : return nScale;
241 : }
242 :
243 : /************************************************************************/
244 : /* GetFromBase34() */
245 : /************************************************************************/
246 :
247 53 : static GIntBig GetFromBase34(const char *pszVal, int nMaxSize)
248 : {
249 53 : GIntBig nFrameNumber = 0;
250 583 : for (int i = 0; i < nMaxSize; i++)
251 : {
252 530 : char ch = pszVal[i];
253 530 : if (ch == '\0')
254 0 : break;
255 : int chVal;
256 530 : if (ch >= 'A' && ch <= 'Z')
257 0 : ch += 'a' - 'A';
258 : /* i and o letters are excluded, */
259 530 : if (ch >= '0' && ch <= '9')
260 477 : chVal = ch - '0';
261 53 : else if (ch >= 'a' && ch <= 'h')
262 0 : chVal = ch - 'a' + 10;
263 53 : else if (ch >= 'j' && ch <= 'n')
264 0 : chVal = ch - 'a' + 10 - 1;
265 53 : else if (ch >= 'p' && ch <= 'z')
266 53 : chVal = ch - 'a' + 10 - 2;
267 : else
268 : {
269 0 : CPLDebug("ECRG", "Invalid base34 value : %s", pszVal);
270 0 : break;
271 : }
272 530 : nFrameNumber = nFrameNumber * 34 + chVal;
273 : }
274 :
275 53 : return nFrameNumber;
276 : }
277 :
278 : /************************************************************************/
279 : /* GetExtent() */
280 : /************************************************************************/
281 :
282 : /* MIL-PRF-32283 - Table II. ECRG zone limits. */
283 : /* starting with a fake zone 0 for convenience. */
284 : constexpr int anZoneUpperLat[] = {0, 32, 48, 56, 64, 68, 72, 76, 80};
285 :
286 : /* APPENDIX 70, TABLE III of MIL-A-89007 */
287 : constexpr int anACst_ADRG[] = {369664, 302592, 245760, 199168,
288 : 163328, 137216, 110080, 82432};
289 : constexpr int nBCst_ADRG = 400384;
290 :
291 : // TODO: Why are these two functions done this way?
292 106 : static int CEIL_ROUND(double a, double b)
293 : {
294 106 : return static_cast<int>(ceil(a / b) * b);
295 : }
296 :
297 106 : static int NEAR_ROUND(double a, double b)
298 : {
299 106 : return static_cast<int>(floor((a / b) + 0.5) * b);
300 : }
301 :
302 : constexpr int ECRG_PIXELS = 2304;
303 :
304 53 : static void GetExtent(const char *pszFrameName, int nScale, int nZone,
305 : double &dfMinX, double &dfMaxX, double &dfMinY,
306 : double &dfMaxY, double &dfPixelXSize,
307 : double &dfPixelYSize)
308 : {
309 53 : const int nAbsZone = abs(nZone);
310 : #ifdef DEBUG
311 53 : assert(nAbsZone > 0 && nAbsZone <= 8);
312 : #endif
313 :
314 : /************************************************************************/
315 : /* Compute east-west constant */
316 : /************************************************************************/
317 : /* MIL-PRF-89038 - 60.1.2 - East-west pixel constant. */
318 : const int nEW_ADRG =
319 53 : CEIL_ROUND(anACst_ADRG[nAbsZone - 1] * (1e6 / nScale), 512);
320 53 : const int nEW_CADRG = NEAR_ROUND(nEW_ADRG / (150. / 100.), 256);
321 : /* MIL-PRF-32283 - D.2.1.2 - East-west pixel constant. */
322 53 : const int nEW = nEW_CADRG / 256 * 384;
323 :
324 : /************************************************************************/
325 : /* Compute number of longitudinal frames */
326 : /************************************************************************/
327 : /* MIL-PRF-32283 - D.2.1.7 - Longitudinal frames and subframes */
328 53 : const int nCols =
329 53 : static_cast<int>(ceil(static_cast<double>(nEW) / ECRG_PIXELS));
330 :
331 : /************************************************************************/
332 : /* Compute north-south constant */
333 : /************************************************************************/
334 : /* MIL-PRF-89038 - 60.1.1 - North-south. pixel constant */
335 53 : const int nNS_ADRG = CEIL_ROUND(nBCst_ADRG * (1e6 / nScale), 512) / 4;
336 53 : const int nNS_CADRG = NEAR_ROUND(nNS_ADRG / (150. / 100.), 256);
337 : /* MIL-PRF-32283 - D.2.1.1 - North-south. pixel constant and Frame
338 : * Width/Height */
339 53 : const int nNS = nNS_CADRG / 256 * 384;
340 :
341 : /************************************************************************/
342 : /* Compute number of latitudinal frames and latitude of top of zone */
343 : /************************************************************************/
344 53 : dfPixelYSize = 90.0 / nNS;
345 :
346 53 : const double dfFrameLatHeight = dfPixelYSize * ECRG_PIXELS;
347 :
348 : /* MIL-PRF-32283 - D.2.1.5 - Equatorward and poleward zone extents. */
349 53 : int nUpperZoneFrames =
350 53 : static_cast<int>(ceil(anZoneUpperLat[nAbsZone] / dfFrameLatHeight));
351 53 : int nBottomZoneFrames = static_cast<int>(
352 53 : floor(anZoneUpperLat[nAbsZone - 1] / dfFrameLatHeight));
353 53 : const int nRows = nUpperZoneFrames - nBottomZoneFrames;
354 :
355 : /* Not sure to really understand D.2.1.5.a. Testing needed */
356 53 : if (nZone < 0)
357 : {
358 0 : nUpperZoneFrames = -nBottomZoneFrames;
359 : /*nBottomZoneFrames = nUpperZoneFrames - nRows;*/
360 : }
361 :
362 53 : const double dfUpperZoneTopLat = dfFrameLatHeight * nUpperZoneFrames;
363 :
364 : /************************************************************************/
365 : /* Compute coordinates of the frame in the zone */
366 : /************************************************************************/
367 :
368 : /* Converts the first 10 characters into a number from base 34 */
369 53 : const GIntBig nFrameNumber = GetFromBase34(pszFrameName, 10);
370 :
371 : /* MIL-PRF-32283 - A.2.6.1 */
372 53 : const GIntBig nY = nFrameNumber / nCols;
373 53 : const GIntBig nX = nFrameNumber % nCols;
374 :
375 : /************************************************************************/
376 : /* Compute extent of the frame */
377 : /************************************************************************/
378 :
379 : /* The nY is counted from the bottom of the zone... Pfff */
380 53 : dfMaxY = dfUpperZoneTopLat - (nRows - 1 - nY) * dfFrameLatHeight;
381 53 : dfMinY = dfMaxY - dfFrameLatHeight;
382 :
383 53 : dfPixelXSize = 360.0 / nEW;
384 :
385 53 : const double dfFrameLongWidth = dfPixelXSize * ECRG_PIXELS;
386 53 : dfMinX = -180.0 + nX * dfFrameLongWidth;
387 53 : dfMaxX = dfMinX + dfFrameLongWidth;
388 :
389 : #ifdef DEBUG_VERBOSE
390 : CPLDebug("ECRG",
391 : "Frame %s : minx=%.16g, maxy=%.16g, maxx=%.16g, miny=%.16g",
392 : pszFrameName, dfMinX, dfMaxY, dfMaxX, dfMinY);
393 : #endif
394 53 : }
395 :
396 : /************************************************************************/
397 : /* ECRGTOCSource */
398 : /************************************************************************/
399 :
400 : class ECRGTOCSource final : public VRTSimpleSource
401 : {
402 : int m_nRasterXSize = 0;
403 : int m_nRasterYSize = 0;
404 : double m_dfMinX = 0;
405 : double m_dfMaxY = 0;
406 : double m_dfPixelXSize = 0;
407 : double m_dfPixelYSize = 0;
408 :
409 : bool ValidateOpenedBand(GDALRasterBand *) const override;
410 :
411 : public:
412 57 : ECRGTOCSource(const char *pszFilename, int nBandIn, int nRasterXSize,
413 : int nRasterYSize, double dfDstXOff, double dfDstYOff,
414 : double dfDstXSize, double dfDstYSize, double dfMinX,
415 : double dfMaxY, double dfPixelXSize, double dfPixelYSize)
416 57 : : m_nRasterXSize(nRasterXSize), m_nRasterYSize(nRasterYSize),
417 : m_dfMinX(dfMinX), m_dfMaxY(dfMaxY), m_dfPixelXSize(dfPixelXSize),
418 57 : m_dfPixelYSize(dfPixelYSize)
419 : {
420 57 : SetSrcBand(pszFilename, nBandIn);
421 57 : SetSrcWindow(0, 0, nRasterXSize, nRasterYSize);
422 57 : SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
423 57 : }
424 : };
425 :
426 : /************************************************************************/
427 : /* ValidateOpenedBand() */
428 : /************************************************************************/
429 :
430 : #define WARN_CHECK_DS(x) \
431 : do \
432 : { \
433 : if (!(x)) \
434 : { \
435 : CPLError(CE_Warning, CPLE_AppDefined, \
436 : "For %s, assert '" #x "' failed", \
437 : poSourceDS->GetDescription()); \
438 : checkOK = false; \
439 : } \
440 : } while (false)
441 :
442 10 : bool ECRGTOCSource::ValidateOpenedBand(GDALRasterBand *poBand) const
443 : {
444 10 : bool checkOK = true;
445 10 : auto poSourceDS = poBand->GetDataset();
446 9 : CPLAssert(poSourceDS);
447 :
448 9 : GDALGeoTransform l_gt;
449 9 : poSourceDS->GetGeoTransform(l_gt);
450 9 : WARN_CHECK_DS(fabs(l_gt[0] - m_dfMinX) < 1e-10);
451 10 : WARN_CHECK_DS(fabs(l_gt[3] - m_dfMaxY) < 1e-10);
452 10 : WARN_CHECK_DS(fabs(l_gt[1] - m_dfPixelXSize) < 1e-10);
453 10 : WARN_CHECK_DS(fabs(l_gt[5] - (-m_dfPixelYSize)) < 1e-10);
454 10 : WARN_CHECK_DS(l_gt[2] == 0 && l_gt[4] == 0); // No rotation.
455 9 : WARN_CHECK_DS(poSourceDS->GetRasterCount() == 3);
456 9 : WARN_CHECK_DS(poSourceDS->GetRasterXSize() == m_nRasterXSize);
457 10 : WARN_CHECK_DS(poSourceDS->GetRasterYSize() == m_nRasterYSize);
458 10 : WARN_CHECK_DS(
459 : EQUAL(poSourceDS->GetProjectionRef(), SRS_WKT_WGS84_LAT_LONG));
460 10 : WARN_CHECK_DS(poSourceDS->GetRasterBand(1)->GetRasterDataType() ==
461 : GDT_Byte);
462 10 : return checkOK;
463 : }
464 :
465 : /************************************************************************/
466 : /* BuildFullName() */
467 : /************************************************************************/
468 :
469 53 : static std::string BuildFullName(const char *pszTOCFilename,
470 : const char *pszFramePath,
471 : const char *pszFrameName)
472 : {
473 53 : char *pszPath = nullptr;
474 53 : if (pszFramePath[0] == '.' &&
475 0 : (pszFramePath[1] == '/' || pszFramePath[1] == '\\'))
476 0 : pszPath = CPLStrdup(pszFramePath + 2);
477 : else
478 53 : pszPath = CPLStrdup(pszFramePath);
479 371 : for (int i = 0; pszPath[i] != '\0'; i++)
480 : {
481 318 : if (pszPath[i] == '\\')
482 53 : pszPath[i] = '/';
483 : }
484 : const std::string osName =
485 106 : CPLFormFilenameSafe(pszPath, pszFrameName, nullptr);
486 53 : CPLFree(pszPath);
487 53 : pszPath = nullptr;
488 106 : std::string osTOCPath = CPLGetDirnameSafe(pszTOCFilename);
489 53 : const auto nPosFirstSlashInName = osName.find('/');
490 53 : if (nPosFirstSlashInName != std::string::npos)
491 : {
492 53 : if (osTOCPath.size() >= nPosFirstSlashInName + 1 &&
493 53 : (osTOCPath[osTOCPath.size() - (nPosFirstSlashInName + 1)] == '/' ||
494 53 : osTOCPath[osTOCPath.size() - (nPosFirstSlashInName + 1)] ==
495 106 : '\\') &&
496 0 : strncmp(osTOCPath.c_str() + osTOCPath.size() - nPosFirstSlashInName,
497 : osName.c_str(), nPosFirstSlashInName) == 0)
498 : {
499 0 : osTOCPath = CPLGetDirnameSafe(osTOCPath.c_str());
500 : }
501 : }
502 106 : return CPLProjectRelativeFilenameSafe(osTOCPath.c_str(), osName.c_str());
503 : }
504 :
505 : /************************************************************************/
506 : /* Build() */
507 : /************************************************************************/
508 :
509 : /* Builds a ECRGTOCSubDataset from the set of files of the toc entry */
510 11 : GDALDataset *ECRGTOCSubDataset::Build(
511 : const char *pszProductTitle, const char *pszDiscId, int nScale,
512 : int nCountSubDataset, const char *pszTOCFilename,
513 : const std::vector<FrameDesc> &aosFrameDesc, double dfGlobalMinX,
514 : double dfGlobalMinY, double dfGlobalMaxX, double dfGlobalMaxY,
515 : double dfGlobalPixelXSize, double dfGlobalPixelYSize)
516 : {
517 11 : GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("VRT");
518 11 : if (poDriver == nullptr)
519 0 : return nullptr;
520 :
521 11 : const int nSizeX = static_cast<int>(
522 11 : (dfGlobalMaxX - dfGlobalMinX) / dfGlobalPixelXSize + 0.5);
523 11 : const int nSizeY = static_cast<int>(
524 11 : (dfGlobalMaxY - dfGlobalMinY) / dfGlobalPixelYSize + 0.5);
525 :
526 : /* ------------------------------------ */
527 : /* Create the VRT with the overall size */
528 : /* ------------------------------------ */
529 11 : ECRGTOCSubDataset *poVirtualDS = new ECRGTOCSubDataset(nSizeX, nSizeY);
530 :
531 11 : poVirtualDS->SetProjection(SRS_WKT_WGS84_LAT_LONG);
532 :
533 : GDALGeoTransform gt{
534 : dfGlobalMinX, dfGlobalPixelXSize, 0, dfGlobalMaxY, 0,
535 11 : -dfGlobalPixelYSize};
536 11 : poVirtualDS->SetGeoTransform(gt);
537 :
538 44 : for (int i = 0; i < 3; i++)
539 : {
540 33 : poVirtualDS->AddBand(GDT_Byte, nullptr);
541 33 : GDALRasterBand *poBand = poVirtualDS->GetRasterBand(i + 1);
542 33 : poBand->SetColorInterpretation(
543 33 : static_cast<GDALColorInterp>(GCI_RedBand + i));
544 : }
545 :
546 11 : poVirtualDS->SetDescription(pszTOCFilename);
547 :
548 11 : poVirtualDS->SetMetadataItem("PRODUCT_TITLE", pszProductTitle);
549 11 : poVirtualDS->SetMetadataItem("DISC_ID", pszDiscId);
550 11 : if (nScale != -1)
551 11 : poVirtualDS->SetMetadataItem("SCALE", CPLString().Printf("%d", nScale));
552 :
553 : /* -------------------------------------------------------------------- */
554 : /* Check for overviews. */
555 : /* -------------------------------------------------------------------- */
556 :
557 11 : poVirtualDS->oOvManager.Initialize(
558 : poVirtualDS,
559 22 : CPLString().Printf("%s.%d", pszTOCFilename, nCountSubDataset));
560 :
561 11 : poVirtualDS->papszFileList = poVirtualDS->GDALDataset::GetFileList();
562 :
563 : // Rather hacky... Force GDAL_FORCE_CACHING=NO so that the
564 : // GDALProxyPoolRasterBand do not use the GDALRasterBand::IRasterIO()
565 : // default implementation, which would rely on the block size of
566 : // GDALProxyPoolRasterBand, which we don't know...
567 11 : CPLConfigOptionSetter oSetter("GDAL_FORCE_CACHING", "NO", false);
568 :
569 30 : for (int i = 0; i < static_cast<int>(aosFrameDesc.size()); i++)
570 : {
571 : const std::string osName = BuildFullName(
572 38 : pszTOCFilename, aosFrameDesc[i].pszPath, aosFrameDesc[i].pszName);
573 :
574 19 : double dfMinX = 0.0;
575 19 : double dfMaxX = 0.0;
576 19 : double dfMinY = 0.0;
577 19 : double dfMaxY = 0.0;
578 19 : double dfPixelXSize = 0.0;
579 19 : double dfPixelYSize = 0.0;
580 19 : ::GetExtent(aosFrameDesc[i].pszName, aosFrameDesc[i].nScale,
581 19 : aosFrameDesc[i].nZone, dfMinX, dfMaxX, dfMinY, dfMaxY,
582 : dfPixelXSize, dfPixelYSize);
583 :
584 19 : const int nFrameXSize =
585 19 : static_cast<int>((dfMaxX - dfMinX) / dfPixelXSize + 0.5);
586 19 : const int nFrameYSize =
587 19 : static_cast<int>((dfMaxY - dfMinY) / dfPixelYSize + 0.5);
588 :
589 19 : poVirtualDS->papszFileList =
590 19 : CSLAddString(poVirtualDS->papszFileList, osName.c_str());
591 :
592 76 : for (int j = 0; j < 3; j++)
593 : {
594 : VRTSourcedRasterBand *poBand =
595 57 : cpl::down_cast<VRTSourcedRasterBand *>(
596 : poVirtualDS->GetRasterBand(j + 1));
597 : /* Place the raster band at the right position in the VRT */
598 : auto poSource = new ECRGTOCSource(
599 57 : osName.c_str(), j + 1, nFrameXSize, nFrameYSize,
600 57 : static_cast<int>((dfMinX - dfGlobalMinX) / dfGlobalPixelXSize +
601 : 0.5),
602 57 : static_cast<int>((dfGlobalMaxY - dfMaxY) / dfGlobalPixelYSize +
603 : 0.5),
604 57 : static_cast<int>((dfMaxX - dfMinX) / dfGlobalPixelXSize + 0.5),
605 57 : static_cast<int>((dfMaxY - dfMinY) / dfGlobalPixelYSize + 0.5),
606 57 : dfMinX, dfMaxY, dfPixelXSize, dfPixelYSize);
607 57 : poBand->AddSource(poSource);
608 : }
609 : }
610 :
611 11 : poVirtualDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
612 :
613 11 : return poVirtualDS;
614 : }
615 :
616 : /************************************************************************/
617 : /* Build() */
618 : /************************************************************************/
619 :
620 21 : GDALDataset *ECRGTOCDataset::Build(const char *pszTOCFilename,
621 : CPLXMLNode *psXML,
622 : const std::string &osProduct,
623 : const std::string &osDiscId,
624 : const std::string &osScale,
625 : const char *pszOpenInfoFilename)
626 : {
627 21 : CPLXMLNode *psTOC = CPLGetXMLNode(psXML, "=Table_of_Contents");
628 21 : if (psTOC == nullptr)
629 : {
630 0 : CPLError(CE_Warning, CPLE_AppDefined,
631 : "Cannot find Table_of_Contents element");
632 0 : return nullptr;
633 : }
634 :
635 21 : double dfGlobalMinX = 0.0;
636 21 : double dfGlobalMinY = 0.0;
637 21 : double dfGlobalMaxX = 0.0;
638 21 : double dfGlobalMaxY = 0.0;
639 21 : double dfGlobalPixelXSize = 0.0;
640 21 : double dfGlobalPixelYSize = 0.0;
641 21 : bool bGlobalExtentValid = false;
642 :
643 21 : ECRGTOCDataset *poDS = new ECRGTOCDataset();
644 21 : int nSubDatasets = 0;
645 :
646 21 : int bLookForSubDataset = !osProduct.empty() && !osDiscId.empty();
647 :
648 21 : int nCountSubDataset = 0;
649 :
650 21 : poDS->SetDescription(pszOpenInfoFilename);
651 21 : poDS->papszFileList = poDS->GDALDataset::GetFileList();
652 :
653 62 : for (CPLXMLNode *psIter1 = psTOC->psChild; psIter1 != nullptr;
654 41 : psIter1 = psIter1->psNext)
655 : {
656 52 : if (!(psIter1->eType == CXT_Element && psIter1->pszValue != nullptr &&
657 52 : strcmp(psIter1->pszValue, "product") == 0))
658 31 : continue;
659 :
660 : const char *pszProductTitle =
661 21 : CPLGetXMLValue(psIter1, "product_title", nullptr);
662 21 : if (pszProductTitle == nullptr)
663 : {
664 0 : CPLError(CE_Warning, CPLE_AppDefined,
665 : "Cannot find product_title attribute");
666 0 : continue;
667 : }
668 :
669 35 : if (bLookForSubDataset &&
670 35 : strcmp(LaunderString(pszProductTitle), osProduct.c_str()) != 0)
671 1 : continue;
672 :
673 51 : for (CPLXMLNode *psIter2 = psIter1->psChild; psIter2 != nullptr;
674 31 : psIter2 = psIter2->psNext)
675 : {
676 42 : if (!(psIter2->eType == CXT_Element &&
677 22 : psIter2->pszValue != nullptr &&
678 22 : strcmp(psIter2->pszValue, "disc") == 0))
679 20 : continue;
680 :
681 22 : const char *pszDiscId = CPLGetXMLValue(psIter2, "id", nullptr);
682 22 : if (pszDiscId == nullptr)
683 : {
684 0 : CPLError(CE_Warning, CPLE_AppDefined,
685 : "Cannot find id attribute");
686 0 : continue;
687 : }
688 :
689 36 : if (bLookForSubDataset &&
690 36 : strcmp(LaunderString(pszDiscId), osDiscId.c_str()) != 0)
691 2 : continue;
692 :
693 20 : CPLXMLNode *psFrameList = CPLGetXMLNode(psIter2, "frame_list");
694 20 : if (psFrameList == nullptr)
695 : {
696 0 : CPLError(CE_Warning, CPLE_AppDefined,
697 : "Cannot find frame_list element");
698 0 : continue;
699 : }
700 :
701 51 : for (CPLXMLNode *psIter3 = psFrameList->psChild; psIter3 != nullptr;
702 31 : psIter3 = psIter3->psNext)
703 : {
704 42 : if (!(psIter3->eType == CXT_Element &&
705 22 : psIter3->pszValue != nullptr &&
706 22 : strcmp(psIter3->pszValue, "scale") == 0))
707 22 : continue;
708 :
709 22 : const char *pszSize = CPLGetXMLValue(psIter3, "size", nullptr);
710 22 : if (pszSize == nullptr)
711 : {
712 0 : CPLError(CE_Warning, CPLE_AppDefined,
713 : "Cannot find size attribute");
714 0 : continue;
715 : }
716 :
717 22 : int nScale = GetScaleFromString(pszSize);
718 22 : if (nScale <= 0)
719 : {
720 0 : CPLError(CE_Warning, CPLE_AppDefined, "Invalid scale %s",
721 : pszSize);
722 0 : continue;
723 : }
724 :
725 22 : if (bLookForSubDataset)
726 : {
727 13 : if (!osScale.empty())
728 : {
729 12 : if (strcmp(LaunderString(pszSize), osScale.c_str()) !=
730 : 0)
731 : {
732 2 : continue;
733 : }
734 : }
735 : else
736 : {
737 1 : int nCountScales = 0;
738 1 : for (CPLXMLNode *psIter4 = psFrameList->psChild;
739 3 : psIter4 != nullptr; psIter4 = psIter4->psNext)
740 : {
741 2 : if (!(psIter4->eType == CXT_Element &&
742 1 : psIter4->pszValue != nullptr &&
743 1 : strcmp(psIter4->pszValue, "scale") == 0))
744 1 : continue;
745 1 : nCountScales++;
746 : }
747 1 : if (nCountScales > 1)
748 : {
749 0 : CPLError(CE_Failure, CPLE_AppDefined,
750 : "Scale should be mentioned in "
751 : "subdatasets syntax since this disk "
752 : "contains several scales");
753 0 : delete poDS;
754 11 : return nullptr;
755 : }
756 : }
757 : }
758 :
759 20 : nCountSubDataset++;
760 :
761 20 : std::vector<FrameDesc> aosFrameDesc;
762 20 : int nValidFrames = 0;
763 :
764 74 : for (CPLXMLNode *psIter4 = psIter3->psChild; psIter4 != nullptr;
765 54 : psIter4 = psIter4->psNext)
766 : {
767 54 : if (!(psIter4->eType == CXT_Element &&
768 34 : psIter4->pszValue != nullptr &&
769 34 : strcmp(psIter4->pszValue, "frame") == 0))
770 20 : continue;
771 :
772 : const char *pszFrameName =
773 34 : CPLGetXMLValue(psIter4, "name", nullptr);
774 34 : if (pszFrameName == nullptr)
775 : {
776 0 : CPLError(CE_Warning, CPLE_AppDefined,
777 : "Cannot find name element");
778 0 : continue;
779 : }
780 :
781 34 : if (strlen(pszFrameName) != 18)
782 : {
783 0 : CPLError(CE_Warning, CPLE_AppDefined,
784 : "Invalid value for name element : %s",
785 : pszFrameName);
786 0 : continue;
787 : }
788 :
789 : const char *pszFramePath =
790 34 : CPLGetXMLValue(psIter4, "frame_path", nullptr);
791 34 : if (pszFramePath == nullptr)
792 : {
793 0 : CPLError(CE_Warning, CPLE_AppDefined,
794 : "Cannot find frame_path element");
795 0 : continue;
796 : }
797 :
798 : const char *pszFrameZone =
799 34 : CPLGetXMLValue(psIter4, "frame_zone", nullptr);
800 34 : if (pszFrameZone == nullptr)
801 : {
802 0 : CPLError(CE_Warning, CPLE_AppDefined,
803 : "Cannot find frame_zone element");
804 0 : continue;
805 : }
806 34 : if (strlen(pszFrameZone) != 1)
807 : {
808 0 : CPLError(CE_Warning, CPLE_AppDefined,
809 : "Invalid value for frame_zone element : %s",
810 : pszFrameZone);
811 0 : continue;
812 : }
813 34 : char chZone = pszFrameZone[0];
814 34 : int nZone = 0;
815 34 : if (chZone >= '1' && chZone <= '9')
816 34 : nZone = chZone - '0';
817 0 : else if (chZone >= 'a' && chZone <= 'h')
818 0 : nZone = -(chZone - 'a' + 1);
819 0 : else if (chZone >= 'A' && chZone <= 'H')
820 0 : nZone = -(chZone - 'A' + 1);
821 0 : else if (chZone == 'j' || chZone == 'J')
822 0 : nZone = -9;
823 : else
824 : {
825 0 : CPLError(CE_Warning, CPLE_AppDefined,
826 : "Invalid value for frame_zone element : %s",
827 : pszFrameZone);
828 0 : continue;
829 : }
830 34 : if (nZone == 9 || nZone == -9)
831 : {
832 0 : CPLError(
833 : CE_Warning, CPLE_AppDefined,
834 : "Polar zones unhandled by current implementation");
835 0 : continue;
836 : }
837 :
838 34 : double dfMinX = 0.0;
839 34 : double dfMaxX = 0.0;
840 34 : double dfMinY = 0.0;
841 34 : double dfMaxY = 0.0;
842 34 : double dfPixelXSize = 0.0;
843 34 : double dfPixelYSize = 0.0;
844 34 : ::GetExtent(pszFrameName, nScale, nZone, dfMinX, dfMaxX,
845 : dfMinY, dfMaxY, dfPixelXSize, dfPixelYSize);
846 :
847 34 : nValidFrames++;
848 :
849 : const std::string osFullName = BuildFullName(
850 68 : pszTOCFilename, pszFramePath, pszFrameName);
851 34 : poDS->papszFileList =
852 34 : CSLAddString(poDS->papszFileList, osFullName.c_str());
853 :
854 34 : if (!bGlobalExtentValid)
855 : {
856 18 : dfGlobalMinX = dfMinX;
857 18 : dfGlobalMinY = dfMinY;
858 18 : dfGlobalMaxX = dfMaxX;
859 18 : dfGlobalMaxY = dfMaxY;
860 18 : dfGlobalPixelXSize = dfPixelXSize;
861 18 : dfGlobalPixelYSize = dfPixelYSize;
862 18 : bGlobalExtentValid = true;
863 : }
864 : else
865 : {
866 16 : if (dfMinX < dfGlobalMinX)
867 0 : dfGlobalMinX = dfMinX;
868 16 : if (dfMinY < dfGlobalMinY)
869 0 : dfGlobalMinY = dfMinY;
870 16 : if (dfMaxX > dfGlobalMaxX)
871 15 : dfGlobalMaxX = dfMaxX;
872 16 : if (dfMaxY > dfGlobalMaxY)
873 1 : dfGlobalMaxY = dfMaxY;
874 16 : if (dfPixelXSize < dfGlobalPixelXSize)
875 0 : dfGlobalPixelXSize = dfPixelXSize;
876 16 : if (dfPixelYSize < dfGlobalPixelYSize)
877 0 : dfGlobalPixelYSize = dfPixelYSize;
878 : }
879 :
880 34 : nValidFrames++;
881 :
882 34 : if (bLookForSubDataset)
883 : {
884 : FrameDesc frameDesc;
885 19 : frameDesc.pszName = pszFrameName;
886 19 : frameDesc.pszPath = pszFramePath;
887 19 : frameDesc.nScale = nScale;
888 19 : frameDesc.nZone = nZone;
889 19 : aosFrameDesc.push_back(frameDesc);
890 : }
891 : }
892 :
893 20 : if (bLookForSubDataset)
894 : {
895 11 : delete poDS;
896 11 : if (nValidFrames == 0)
897 0 : return nullptr;
898 11 : return ECRGTOCSubDataset::Build(
899 : pszProductTitle, pszDiscId, nScale, nCountSubDataset,
900 : pszTOCFilename, aosFrameDesc, dfGlobalMinX,
901 : dfGlobalMinY, dfGlobalMaxX, dfGlobalMaxY,
902 11 : dfGlobalPixelXSize, dfGlobalPixelYSize);
903 : }
904 :
905 9 : if (nValidFrames)
906 : {
907 9 : poDS->AddSubDataset(pszOpenInfoFilename, pszProductTitle,
908 : pszDiscId, pszSize);
909 9 : nSubDatasets++;
910 : }
911 : }
912 : }
913 : }
914 :
915 10 : if (!bGlobalExtentValid)
916 : {
917 3 : delete poDS;
918 3 : return nullptr;
919 : }
920 :
921 7 : if (nSubDatasets == 1)
922 : {
923 12 : const char *pszSubDatasetName = CSLFetchNameValue(
924 6 : poDS->GetMetadata("SUBDATASETS"), "SUBDATASET_1_NAME");
925 6 : GDALOpenInfo oOpenInfo(pszSubDatasetName, GA_ReadOnly);
926 6 : delete poDS;
927 6 : GDALDataset *poRetDS = Open(&oOpenInfo);
928 6 : if (poRetDS)
929 6 : poRetDS->SetDescription(pszOpenInfoFilename);
930 6 : return poRetDS;
931 : }
932 :
933 1 : poDS->m_gt[0] = dfGlobalMinX;
934 1 : poDS->m_gt[1] = dfGlobalPixelXSize;
935 1 : poDS->m_gt[2] = 0.0;
936 1 : poDS->m_gt[3] = dfGlobalMaxY;
937 1 : poDS->m_gt[4] = 0.0;
938 1 : poDS->m_gt[5] = -dfGlobalPixelYSize;
939 :
940 1 : poDS->nRasterXSize = static_cast<int>(0.5 + (dfGlobalMaxX - dfGlobalMinX) /
941 : dfGlobalPixelXSize);
942 1 : poDS->nRasterYSize = static_cast<int>(0.5 + (dfGlobalMaxY - dfGlobalMinY) /
943 : dfGlobalPixelYSize);
944 :
945 : /* -------------------------------------------------------------------- */
946 : /* Initialize any PAM information. */
947 : /* -------------------------------------------------------------------- */
948 1 : poDS->TryLoadXML();
949 :
950 1 : return poDS;
951 : }
952 :
953 : /************************************************************************/
954 : /* Open() */
955 : /************************************************************************/
956 :
957 30 : GDALDataset *ECRGTOCDataset::Open(GDALOpenInfo *poOpenInfo)
958 :
959 : {
960 30 : if (!ECRGTOCDriverIdentify(poOpenInfo))
961 0 : return nullptr;
962 :
963 30 : const char *pszFilename = poOpenInfo->pszFilename;
964 60 : CPLString osFilename;
965 60 : CPLString osProduct, osDiscId, osScale;
966 :
967 30 : if (STARTS_WITH_CI(pszFilename, "ECRG_TOC_ENTRY:"))
968 : {
969 23 : pszFilename += strlen("ECRG_TOC_ENTRY:");
970 :
971 : /* PRODUCT:DISK:SCALE:FILENAME (or PRODUCT:DISK:FILENAME historically)
972 : */
973 : /* with FILENAME potentially C:\BLA... */
974 23 : char **papszTokens = CSLTokenizeString2(pszFilename, ":", 0);
975 23 : int nTokens = CSLCount(papszTokens);
976 23 : if (nTokens != 3 && nTokens != 4 && nTokens != 5)
977 : {
978 4 : CSLDestroy(papszTokens);
979 4 : return nullptr;
980 : }
981 :
982 19 : osProduct = papszTokens[0];
983 19 : osDiscId = papszTokens[1];
984 :
985 19 : if (nTokens == 3)
986 5 : osFilename = papszTokens[2];
987 14 : else if (nTokens == 4)
988 : {
989 13 : if (strlen(papszTokens[2]) == 1 &&
990 1 : (papszTokens[3][0] == '\\' || papszTokens[3][0] == '/'))
991 : {
992 1 : osFilename = papszTokens[2];
993 1 : osFilename += ":";
994 1 : osFilename += papszTokens[3];
995 : }
996 : else
997 : {
998 12 : osScale = papszTokens[2];
999 12 : osFilename = papszTokens[3];
1000 : }
1001 : }
1002 1 : else if (nTokens == 5 && strlen(papszTokens[3]) == 1 &&
1003 1 : (papszTokens[4][0] == '\\' || papszTokens[4][0] == '/'))
1004 : {
1005 1 : osScale = papszTokens[2];
1006 1 : osFilename = papszTokens[3];
1007 1 : osFilename += ":";
1008 1 : osFilename += papszTokens[4];
1009 : }
1010 : else
1011 : {
1012 0 : CSLDestroy(papszTokens);
1013 0 : return nullptr;
1014 : }
1015 :
1016 19 : CSLDestroy(papszTokens);
1017 19 : pszFilename = osFilename.c_str();
1018 : }
1019 :
1020 : /* -------------------------------------------------------------------- */
1021 : /* Parse the XML file */
1022 : /* -------------------------------------------------------------------- */
1023 26 : CPLXMLNode *psXML = CPLParseXMLFile(pszFilename);
1024 26 : if (psXML == nullptr)
1025 : {
1026 5 : return nullptr;
1027 : }
1028 :
1029 42 : GDALDataset *poDS = Build(pszFilename, psXML, osProduct, osDiscId, osScale,
1030 21 : poOpenInfo->pszFilename);
1031 21 : CPLDestroyXMLNode(psXML);
1032 :
1033 21 : if (poDS && poOpenInfo->eAccess == GA_Update)
1034 : {
1035 0 : ReportUpdateNotSupportedByDriver("ECRGTOC");
1036 0 : delete poDS;
1037 0 : return nullptr;
1038 : }
1039 :
1040 21 : return poDS;
1041 : }
1042 :
1043 : /************************************************************************/
1044 : /* GDALRegister_ECRGTOC() */
1045 : /************************************************************************/
1046 :
1047 1961 : void GDALRegister_ECRGTOC()
1048 :
1049 : {
1050 1961 : if (GDALGetDriverByName(ECRGTOC_DRIVER_NAME) != nullptr)
1051 283 : return;
1052 :
1053 1678 : GDALDriver *poDriver = new GDALDriver();
1054 1678 : ECRGTOCDriverSetCommonMetadata(poDriver);
1055 :
1056 1678 : poDriver->pfnOpen = ECRGTOCDataset::Open;
1057 :
1058 1678 : GetGDALDriverManager()->RegisterDriver(poDriver);
1059 : }
|