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 : ~ECRGTOCDataset() override
80 21 : {
81 21 : CSLDestroy(papszSubDatasets);
82 21 : CSLDestroy(papszFileList);
83 42 : }
84 :
85 : char **GetMetadata(const char *pszDomain = "") override;
86 :
87 1 : 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 : 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 : 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 10 : CPLAssert(poSourceDS);
447 :
448 10 : GDALGeoTransform l_gt;
449 10 : poSourceDS->GetGeoTransform(l_gt);
450 10 : 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 10 : WARN_CHECK_DS(poSourceDS->GetRasterCount() == 3);
456 10 : 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 22 : auto poVirtualDS = std::make_unique<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 22 : poVirtualDS->oOvManager.Initialize(
558 11 : poVirtualDS.get(),
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 22 : CPLConfigOptionSetter oSetter("GDAL_FORCE_CACHING", "NO", false);
568 :
569 30 : for (int i = 0; i < static_cast<int>(aosFrameDesc.size()); i++)
570 : {
571 19 : if (CPLHasPathTraversal(aosFrameDesc[i].pszName))
572 : {
573 0 : CPLError(CE_Failure, CPLE_AppDefined,
574 0 : "Path traversal detected in %s", aosFrameDesc[i].pszName);
575 0 : return nullptr;
576 : }
577 : const std::string osName = BuildFullName(
578 38 : pszTOCFilename, aosFrameDesc[i].pszPath, aosFrameDesc[i].pszName);
579 :
580 19 : double dfMinX = 0.0;
581 19 : double dfMaxX = 0.0;
582 19 : double dfMinY = 0.0;
583 19 : double dfMaxY = 0.0;
584 19 : double dfPixelXSize = 0.0;
585 19 : double dfPixelYSize = 0.0;
586 19 : ::GetExtent(aosFrameDesc[i].pszName, aosFrameDesc[i].nScale,
587 19 : aosFrameDesc[i].nZone, dfMinX, dfMaxX, dfMinY, dfMaxY,
588 : dfPixelXSize, dfPixelYSize);
589 :
590 19 : const int nFrameXSize =
591 19 : static_cast<int>((dfMaxX - dfMinX) / dfPixelXSize + 0.5);
592 19 : const int nFrameYSize =
593 19 : static_cast<int>((dfMaxY - dfMinY) / dfPixelYSize + 0.5);
594 :
595 38 : poVirtualDS->papszFileList =
596 19 : CSLAddString(poVirtualDS->papszFileList, osName.c_str());
597 :
598 76 : for (int j = 0; j < 3; j++)
599 : {
600 : VRTSourcedRasterBand *poBand =
601 57 : cpl::down_cast<VRTSourcedRasterBand *>(
602 57 : poVirtualDS->GetRasterBand(j + 1));
603 : /* Place the raster band at the right position in the VRT */
604 : auto poSource = new ECRGTOCSource(
605 57 : osName.c_str(), j + 1, nFrameXSize, nFrameYSize,
606 57 : static_cast<int>((dfMinX - dfGlobalMinX) / dfGlobalPixelXSize +
607 : 0.5),
608 57 : static_cast<int>((dfGlobalMaxY - dfMaxY) / dfGlobalPixelYSize +
609 : 0.5),
610 57 : static_cast<int>((dfMaxX - dfMinX) / dfGlobalPixelXSize + 0.5),
611 57 : static_cast<int>((dfMaxY - dfMinY) / dfGlobalPixelYSize + 0.5),
612 57 : dfMinX, dfMaxY, dfPixelXSize, dfPixelYSize);
613 57 : poBand->AddSource(poSource);
614 : }
615 : }
616 :
617 11 : poVirtualDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
618 :
619 11 : return poVirtualDS.release();
620 : }
621 :
622 : /************************************************************************/
623 : /* Build() */
624 : /************************************************************************/
625 :
626 21 : GDALDataset *ECRGTOCDataset::Build(const char *pszTOCFilename,
627 : CPLXMLNode *psXML,
628 : const std::string &osProduct,
629 : const std::string &osDiscId,
630 : const std::string &osScale,
631 : const char *pszOpenInfoFilename)
632 : {
633 21 : CPLXMLNode *psTOC = CPLGetXMLNode(psXML, "=Table_of_Contents");
634 21 : if (psTOC == nullptr)
635 : {
636 0 : CPLError(CE_Warning, CPLE_AppDefined,
637 : "Cannot find Table_of_Contents element");
638 0 : return nullptr;
639 : }
640 :
641 21 : double dfGlobalMinX = 0.0;
642 21 : double dfGlobalMinY = 0.0;
643 21 : double dfGlobalMaxX = 0.0;
644 21 : double dfGlobalMaxY = 0.0;
645 21 : double dfGlobalPixelXSize = 0.0;
646 21 : double dfGlobalPixelYSize = 0.0;
647 21 : bool bGlobalExtentValid = false;
648 :
649 42 : auto poDS = std::make_unique<ECRGTOCDataset>();
650 21 : int nSubDatasets = 0;
651 :
652 21 : int bLookForSubDataset = !osProduct.empty() && !osDiscId.empty();
653 :
654 21 : int nCountSubDataset = 0;
655 :
656 21 : poDS->SetDescription(pszOpenInfoFilename);
657 21 : poDS->papszFileList = poDS->GDALDataset::GetFileList();
658 :
659 62 : for (CPLXMLNode *psIter1 = psTOC->psChild; psIter1 != nullptr;
660 41 : psIter1 = psIter1->psNext)
661 : {
662 52 : if (!(psIter1->eType == CXT_Element && psIter1->pszValue != nullptr &&
663 52 : strcmp(psIter1->pszValue, "product") == 0))
664 31 : continue;
665 :
666 : const char *pszProductTitle =
667 21 : CPLGetXMLValue(psIter1, "product_title", nullptr);
668 21 : if (pszProductTitle == nullptr)
669 : {
670 0 : CPLError(CE_Warning, CPLE_AppDefined,
671 : "Cannot find product_title attribute");
672 0 : continue;
673 : }
674 :
675 35 : if (bLookForSubDataset &&
676 35 : strcmp(LaunderString(pszProductTitle), osProduct.c_str()) != 0)
677 1 : continue;
678 :
679 51 : for (CPLXMLNode *psIter2 = psIter1->psChild; psIter2 != nullptr;
680 31 : psIter2 = psIter2->psNext)
681 : {
682 42 : if (!(psIter2->eType == CXT_Element &&
683 22 : psIter2->pszValue != nullptr &&
684 22 : strcmp(psIter2->pszValue, "disc") == 0))
685 20 : continue;
686 :
687 22 : const char *pszDiscId = CPLGetXMLValue(psIter2, "id", nullptr);
688 22 : if (pszDiscId == nullptr)
689 : {
690 0 : CPLError(CE_Warning, CPLE_AppDefined,
691 : "Cannot find id attribute");
692 0 : continue;
693 : }
694 :
695 36 : if (bLookForSubDataset &&
696 36 : strcmp(LaunderString(pszDiscId), osDiscId.c_str()) != 0)
697 2 : continue;
698 :
699 20 : CPLXMLNode *psFrameList = CPLGetXMLNode(psIter2, "frame_list");
700 20 : if (psFrameList == nullptr)
701 : {
702 0 : CPLError(CE_Warning, CPLE_AppDefined,
703 : "Cannot find frame_list element");
704 0 : continue;
705 : }
706 :
707 51 : for (CPLXMLNode *psIter3 = psFrameList->psChild; psIter3 != nullptr;
708 31 : psIter3 = psIter3->psNext)
709 : {
710 42 : if (!(psIter3->eType == CXT_Element &&
711 22 : psIter3->pszValue != nullptr &&
712 22 : strcmp(psIter3->pszValue, "scale") == 0))
713 22 : continue;
714 :
715 22 : const char *pszSize = CPLGetXMLValue(psIter3, "size", nullptr);
716 22 : if (pszSize == nullptr)
717 : {
718 0 : CPLError(CE_Warning, CPLE_AppDefined,
719 : "Cannot find size attribute");
720 0 : continue;
721 : }
722 :
723 22 : int nScale = GetScaleFromString(pszSize);
724 22 : if (nScale <= 0)
725 : {
726 0 : CPLError(CE_Warning, CPLE_AppDefined, "Invalid scale %s",
727 : pszSize);
728 0 : continue;
729 : }
730 :
731 22 : if (bLookForSubDataset)
732 : {
733 13 : if (!osScale.empty())
734 : {
735 12 : if (strcmp(LaunderString(pszSize), osScale.c_str()) !=
736 : 0)
737 : {
738 2 : continue;
739 : }
740 : }
741 : else
742 : {
743 1 : int nCountScales = 0;
744 1 : for (CPLXMLNode *psIter4 = psFrameList->psChild;
745 3 : psIter4 != nullptr; psIter4 = psIter4->psNext)
746 : {
747 2 : if (!(psIter4->eType == CXT_Element &&
748 1 : psIter4->pszValue != nullptr &&
749 1 : strcmp(psIter4->pszValue, "scale") == 0))
750 1 : continue;
751 1 : nCountScales++;
752 : }
753 1 : if (nCountScales > 1)
754 : {
755 0 : CPLError(CE_Failure, CPLE_AppDefined,
756 : "Scale should be mentioned in "
757 : "subdatasets syntax since this disk "
758 : "contains several scales");
759 11 : return nullptr;
760 : }
761 : }
762 : }
763 :
764 20 : nCountSubDataset++;
765 :
766 20 : std::vector<FrameDesc> aosFrameDesc;
767 20 : int nValidFrames = 0;
768 :
769 74 : for (CPLXMLNode *psIter4 = psIter3->psChild; psIter4 != nullptr;
770 54 : psIter4 = psIter4->psNext)
771 : {
772 54 : if (!(psIter4->eType == CXT_Element &&
773 34 : psIter4->pszValue != nullptr &&
774 34 : strcmp(psIter4->pszValue, "frame") == 0))
775 20 : continue;
776 :
777 : const char *pszFrameName =
778 34 : CPLGetXMLValue(psIter4, "name", nullptr);
779 34 : if (pszFrameName == nullptr)
780 : {
781 0 : CPLError(CE_Warning, CPLE_AppDefined,
782 : "Cannot find name element");
783 0 : continue;
784 : }
785 :
786 34 : if (strlen(pszFrameName) != 18)
787 : {
788 0 : CPLError(CE_Warning, CPLE_AppDefined,
789 : "Invalid value for name element : %s",
790 : pszFrameName);
791 0 : continue;
792 : }
793 :
794 : const char *pszFramePath =
795 34 : CPLGetXMLValue(psIter4, "frame_path", nullptr);
796 34 : if (pszFramePath == nullptr)
797 : {
798 0 : CPLError(CE_Warning, CPLE_AppDefined,
799 : "Cannot find frame_path element");
800 0 : continue;
801 : }
802 :
803 : const char *pszFrameZone =
804 34 : CPLGetXMLValue(psIter4, "frame_zone", nullptr);
805 34 : if (pszFrameZone == nullptr)
806 : {
807 0 : CPLError(CE_Warning, CPLE_AppDefined,
808 : "Cannot find frame_zone element");
809 0 : continue;
810 : }
811 34 : if (strlen(pszFrameZone) != 1)
812 : {
813 0 : CPLError(CE_Warning, CPLE_AppDefined,
814 : "Invalid value for frame_zone element : %s",
815 : pszFrameZone);
816 0 : continue;
817 : }
818 34 : char chZone = pszFrameZone[0];
819 34 : int nZone = 0;
820 34 : if (chZone >= '1' && chZone <= '9')
821 34 : nZone = chZone - '0';
822 0 : else if (chZone >= 'a' && chZone <= 'h')
823 0 : nZone = -(chZone - 'a' + 1);
824 0 : else if (chZone >= 'A' && chZone <= 'H')
825 0 : nZone = -(chZone - 'A' + 1);
826 0 : else if (chZone == 'j' || chZone == 'J')
827 0 : nZone = -9;
828 : else
829 : {
830 0 : CPLError(CE_Warning, CPLE_AppDefined,
831 : "Invalid value for frame_zone element : %s",
832 : pszFrameZone);
833 0 : continue;
834 : }
835 34 : if (nZone == 9 || nZone == -9)
836 : {
837 0 : CPLError(
838 : CE_Warning, CPLE_AppDefined,
839 : "Polar zones unhandled by current implementation");
840 0 : continue;
841 : }
842 :
843 34 : double dfMinX = 0.0;
844 34 : double dfMaxX = 0.0;
845 34 : double dfMinY = 0.0;
846 34 : double dfMaxY = 0.0;
847 34 : double dfPixelXSize = 0.0;
848 34 : double dfPixelYSize = 0.0;
849 34 : ::GetExtent(pszFrameName, nScale, nZone, dfMinX, dfMaxX,
850 : dfMinY, dfMaxY, dfPixelXSize, dfPixelYSize);
851 :
852 34 : nValidFrames++;
853 :
854 34 : if (CPLHasPathTraversal(pszFrameName))
855 : {
856 0 : CPLError(CE_Failure, CPLE_AppDefined,
857 : "Path traversal detected in %s", pszFrameName);
858 0 : return nullptr;
859 : }
860 : const std::string osFullName = BuildFullName(
861 68 : pszTOCFilename, pszFramePath, pszFrameName);
862 68 : poDS->papszFileList =
863 34 : CSLAddString(poDS->papszFileList, osFullName.c_str());
864 :
865 34 : if (!bGlobalExtentValid)
866 : {
867 18 : dfGlobalMinX = dfMinX;
868 18 : dfGlobalMinY = dfMinY;
869 18 : dfGlobalMaxX = dfMaxX;
870 18 : dfGlobalMaxY = dfMaxY;
871 18 : dfGlobalPixelXSize = dfPixelXSize;
872 18 : dfGlobalPixelYSize = dfPixelYSize;
873 18 : bGlobalExtentValid = true;
874 : }
875 : else
876 : {
877 16 : if (dfMinX < dfGlobalMinX)
878 0 : dfGlobalMinX = dfMinX;
879 16 : if (dfMinY < dfGlobalMinY)
880 0 : dfGlobalMinY = dfMinY;
881 16 : if (dfMaxX > dfGlobalMaxX)
882 15 : dfGlobalMaxX = dfMaxX;
883 16 : if (dfMaxY > dfGlobalMaxY)
884 1 : dfGlobalMaxY = dfMaxY;
885 16 : if (dfPixelXSize < dfGlobalPixelXSize)
886 0 : dfGlobalPixelXSize = dfPixelXSize;
887 16 : if (dfPixelYSize < dfGlobalPixelYSize)
888 0 : dfGlobalPixelYSize = dfPixelYSize;
889 : }
890 :
891 34 : nValidFrames++;
892 :
893 34 : if (bLookForSubDataset)
894 : {
895 : FrameDesc frameDesc;
896 19 : frameDesc.pszName = pszFrameName;
897 19 : frameDesc.pszPath = pszFramePath;
898 19 : frameDesc.nScale = nScale;
899 19 : frameDesc.nZone = nZone;
900 19 : aosFrameDesc.push_back(frameDesc);
901 : }
902 : }
903 :
904 20 : if (bLookForSubDataset)
905 : {
906 11 : if (nValidFrames == 0)
907 0 : return nullptr;
908 11 : return ECRGTOCSubDataset::Build(
909 : pszProductTitle, pszDiscId, nScale, nCountSubDataset,
910 : pszTOCFilename, aosFrameDesc, dfGlobalMinX,
911 : dfGlobalMinY, dfGlobalMaxX, dfGlobalMaxY,
912 11 : dfGlobalPixelXSize, dfGlobalPixelYSize);
913 : }
914 :
915 9 : if (nValidFrames)
916 : {
917 9 : poDS->AddSubDataset(pszOpenInfoFilename, pszProductTitle,
918 : pszDiscId, pszSize);
919 9 : nSubDatasets++;
920 : }
921 : }
922 : }
923 : }
924 :
925 10 : if (!bGlobalExtentValid)
926 : {
927 3 : return nullptr;
928 : }
929 :
930 7 : if (nSubDatasets == 1)
931 : {
932 12 : const char *pszSubDatasetName = CSLFetchNameValue(
933 6 : poDS->GetMetadata("SUBDATASETS"), "SUBDATASET_1_NAME");
934 6 : GDALOpenInfo oOpenInfo(pszSubDatasetName, GA_ReadOnly);
935 6 : poDS.reset();
936 6 : GDALDataset *poRetDS = Open(&oOpenInfo);
937 6 : if (poRetDS)
938 6 : poRetDS->SetDescription(pszOpenInfoFilename);
939 6 : return poRetDS;
940 : }
941 :
942 1 : poDS->m_gt[0] = dfGlobalMinX;
943 1 : poDS->m_gt[1] = dfGlobalPixelXSize;
944 1 : poDS->m_gt[2] = 0.0;
945 1 : poDS->m_gt[3] = dfGlobalMaxY;
946 1 : poDS->m_gt[4] = 0.0;
947 1 : poDS->m_gt[5] = -dfGlobalPixelYSize;
948 :
949 1 : poDS->nRasterXSize = static_cast<int>(0.5 + (dfGlobalMaxX - dfGlobalMinX) /
950 : dfGlobalPixelXSize);
951 1 : poDS->nRasterYSize = static_cast<int>(0.5 + (dfGlobalMaxY - dfGlobalMinY) /
952 : dfGlobalPixelYSize);
953 :
954 : /* -------------------------------------------------------------------- */
955 : /* Initialize any PAM information. */
956 : /* -------------------------------------------------------------------- */
957 1 : poDS->TryLoadXML();
958 :
959 1 : return poDS.release();
960 : }
961 :
962 : /************************************************************************/
963 : /* Open() */
964 : /************************************************************************/
965 :
966 30 : GDALDataset *ECRGTOCDataset::Open(GDALOpenInfo *poOpenInfo)
967 :
968 : {
969 30 : if (!ECRGTOCDriverIdentify(poOpenInfo))
970 0 : return nullptr;
971 :
972 30 : const char *pszFilename = poOpenInfo->pszFilename;
973 60 : CPLString osFilename;
974 60 : CPLString osProduct, osDiscId, osScale;
975 :
976 30 : if (STARTS_WITH_CI(pszFilename, "ECRG_TOC_ENTRY:"))
977 : {
978 23 : pszFilename += strlen("ECRG_TOC_ENTRY:");
979 :
980 : /* PRODUCT:DISK:SCALE:FILENAME (or PRODUCT:DISK:FILENAME historically)
981 : */
982 : /* with FILENAME potentially C:\BLA... */
983 23 : char **papszTokens = CSLTokenizeString2(pszFilename, ":", 0);
984 23 : int nTokens = CSLCount(papszTokens);
985 23 : if (nTokens != 3 && nTokens != 4 && nTokens != 5)
986 : {
987 4 : CSLDestroy(papszTokens);
988 4 : return nullptr;
989 : }
990 :
991 19 : osProduct = papszTokens[0];
992 19 : osDiscId = papszTokens[1];
993 :
994 19 : if (nTokens == 3)
995 5 : osFilename = papszTokens[2];
996 14 : else if (nTokens == 4)
997 : {
998 13 : if (strlen(papszTokens[2]) == 1 &&
999 1 : (papszTokens[3][0] == '\\' || papszTokens[3][0] == '/'))
1000 : {
1001 1 : osFilename = papszTokens[2];
1002 1 : osFilename += ":";
1003 1 : osFilename += papszTokens[3];
1004 : }
1005 : else
1006 : {
1007 12 : osScale = papszTokens[2];
1008 12 : osFilename = papszTokens[3];
1009 : }
1010 : }
1011 1 : else if (nTokens == 5 && strlen(papszTokens[3]) == 1 &&
1012 1 : (papszTokens[4][0] == '\\' || papszTokens[4][0] == '/'))
1013 : {
1014 1 : osScale = papszTokens[2];
1015 1 : osFilename = papszTokens[3];
1016 1 : osFilename += ":";
1017 1 : osFilename += papszTokens[4];
1018 : }
1019 : else
1020 : {
1021 0 : CSLDestroy(papszTokens);
1022 0 : return nullptr;
1023 : }
1024 :
1025 19 : CSLDestroy(papszTokens);
1026 19 : pszFilename = osFilename.c_str();
1027 : }
1028 :
1029 : /* -------------------------------------------------------------------- */
1030 : /* Parse the XML file */
1031 : /* -------------------------------------------------------------------- */
1032 26 : CPLXMLNode *psXML = CPLParseXMLFile(pszFilename);
1033 26 : if (psXML == nullptr)
1034 : {
1035 5 : return nullptr;
1036 : }
1037 :
1038 42 : GDALDataset *poDS = Build(pszFilename, psXML, osProduct, osDiscId, osScale,
1039 21 : poOpenInfo->pszFilename);
1040 21 : CPLDestroyXMLNode(psXML);
1041 :
1042 21 : if (poDS && poOpenInfo->eAccess == GA_Update)
1043 : {
1044 0 : ReportUpdateNotSupportedByDriver("ECRGTOC");
1045 0 : delete poDS;
1046 0 : return nullptr;
1047 : }
1048 :
1049 21 : return poDS;
1050 : }
1051 :
1052 : /************************************************************************/
1053 : /* GDALRegister_ECRGTOC() */
1054 : /************************************************************************/
1055 :
1056 2024 : void GDALRegister_ECRGTOC()
1057 :
1058 : {
1059 2024 : if (GDALGetDriverByName(ECRGTOC_DRIVER_NAME) != nullptr)
1060 283 : return;
1061 :
1062 1741 : GDALDriver *poDriver = new GDALDriver();
1063 1741 : ECRGTOCDriverSetCommonMetadata(poDriver);
1064 :
1065 1741 : poDriver->pfnOpen = ECRGTOCDataset::Open;
1066 :
1067 1741 : GetGDALDriverManager()->RegisterDriver(poDriver);
1068 : }
|