/src/gdal/frmts/nitf/ecrgtocdataset.cpp
Line | Count | Source |
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 | | ECRGTOCDataset() |
74 | 0 | { |
75 | 0 | m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
76 | 0 | m_oSRS.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG); |
77 | 0 | } |
78 | | |
79 | | ~ECRGTOCDataset() override |
80 | 0 | { |
81 | 0 | CSLDestroy(papszSubDatasets); |
82 | 0 | CSLDestroy(papszFileList); |
83 | 0 | } |
84 | | |
85 | | CSLConstList GetMetadata(const char *pszDomain = "") override; |
86 | | |
87 | | char **GetFileList() override |
88 | 0 | { |
89 | 0 | return CSLDuplicate(papszFileList); |
90 | 0 | } |
91 | | |
92 | | void AddSubDataset(const char *pszFilename, const char *pszProductTitle, |
93 | | const char *pszDiscId, const char *pszScale); |
94 | | |
95 | | CPLErr GetGeoTransform(GDALGeoTransform >) const override |
96 | 0 | { |
97 | 0 | gt = m_gt; |
98 | 0 | return CE_None; |
99 | 0 | } |
100 | | |
101 | | const OGRSpatialReference *GetSpatialRef() const override |
102 | 0 | { |
103 | 0 | return &m_oSRS; |
104 | 0 | } |
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 | 0 | ECRGTOCSubDataset(int nXSize, int nYSize) : VRTDataset(nXSize, nYSize) |
128 | 0 | { |
129 | | /* Don't try to write a VRT file */ |
130 | 0 | SetWritable(FALSE); |
131 | | |
132 | | /* The driver is set to VRT in VRTDataset constructor. */ |
133 | | /* We have to set it to the expected value ! */ |
134 | 0 | poDriver = GDALDriver::FromHandle(GDALGetDriverByName("ECRGTOC")); |
135 | 0 | } |
136 | | |
137 | | ~ECRGTOCSubDataset() override; |
138 | | |
139 | | char **GetFileList() override |
140 | 0 | { |
141 | 0 | return CSLDuplicate(papszFileList); |
142 | 0 | } |
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 | | ECRGTOCSubDataset::~ECRGTOCSubDataset() |
153 | 0 | { |
154 | 0 | CSLDestroy(papszFileList); |
155 | 0 | } |
156 | | |
157 | | /************************************************************************/ |
158 | | /* LaunderString() */ |
159 | | /************************************************************************/ |
160 | | |
161 | | static CPLString LaunderString(const char *pszStr) |
162 | 0 | { |
163 | 0 | CPLString osRet(pszStr); |
164 | 0 | for (size_t i = 0; i < osRet.size(); i++) |
165 | 0 | { |
166 | 0 | if (osRet[i] == ':' || osRet[i] == ' ') |
167 | 0 | osRet[i] = '_'; |
168 | 0 | } |
169 | 0 | return osRet; |
170 | 0 | } |
171 | | |
172 | | /************************************************************************/ |
173 | | /* AddSubDataset() */ |
174 | | /************************************************************************/ |
175 | | |
176 | | void ECRGTOCDataset::AddSubDataset(const char *pszFilename, |
177 | | const char *pszProductTitle, |
178 | | const char *pszDiscId, const char *pszScale) |
179 | | |
180 | 0 | { |
181 | 0 | char szName[80]; |
182 | 0 | const int nCount = CSLCount(papszSubDatasets) / 2; |
183 | |
|
184 | 0 | snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1); |
185 | 0 | papszSubDatasets = CSLSetNameValue( |
186 | 0 | papszSubDatasets, szName, |
187 | 0 | CPLSPrintf("ECRG_TOC_ENTRY:%s:%s:%s:%s", |
188 | 0 | LaunderString(pszProductTitle).c_str(), |
189 | 0 | LaunderString(pszDiscId).c_str(), |
190 | 0 | LaunderString(pszScale).c_str(), pszFilename)); |
191 | |
|
192 | 0 | snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1); |
193 | 0 | papszSubDatasets = |
194 | 0 | CSLSetNameValue(papszSubDatasets, szName, |
195 | 0 | CPLSPrintf("Product %s, disc %s, scale %s", |
196 | 0 | pszProductTitle, pszDiscId, pszScale)); |
197 | 0 | } |
198 | | |
199 | | /************************************************************************/ |
200 | | /* GetMetadata() */ |
201 | | /************************************************************************/ |
202 | | |
203 | | CSLConstList ECRGTOCDataset::GetMetadata(const char *pszDomain) |
204 | | |
205 | 0 | { |
206 | 0 | if (pszDomain != nullptr && EQUAL(pszDomain, GDAL_MDD_SUBDATASETS)) |
207 | 0 | return papszSubDatasets; |
208 | | |
209 | 0 | return GDALPamDataset::GetMetadata(pszDomain); |
210 | 0 | } |
211 | | |
212 | | /************************************************************************/ |
213 | | /* GetScaleFromString() */ |
214 | | /************************************************************************/ |
215 | | |
216 | | static int GetScaleFromString(const char *pszScale) |
217 | 0 | { |
218 | 0 | const char *pszPtr = strstr(pszScale, "1:"); |
219 | 0 | if (pszPtr) |
220 | 0 | pszPtr = pszPtr + 2; |
221 | 0 | else |
222 | 0 | pszPtr = pszScale; |
223 | |
|
224 | 0 | int nScale = 0; |
225 | 0 | char ch; |
226 | 0 | while ((ch = *pszPtr) != '\0') |
227 | 0 | { |
228 | 0 | if (ch >= '0' && ch <= '9') |
229 | 0 | nScale = nScale * 10 + ch - '0'; |
230 | 0 | else if (ch == ' ') |
231 | 0 | ; |
232 | 0 | else if (ch == 'k' || ch == 'K') |
233 | 0 | return nScale * 1000; |
234 | 0 | else if (ch == 'm' || ch == 'M') |
235 | 0 | return nScale * 1000000; |
236 | 0 | else |
237 | 0 | return 0; |
238 | 0 | pszPtr++; |
239 | 0 | } |
240 | 0 | return nScale; |
241 | 0 | } |
242 | | |
243 | | /************************************************************************/ |
244 | | /* GetFromBase34() */ |
245 | | /************************************************************************/ |
246 | | |
247 | | static GIntBig GetFromBase34(const char *pszVal, int nMaxSize) |
248 | 0 | { |
249 | 0 | GIntBig nFrameNumber = 0; |
250 | 0 | for (int i = 0; i < nMaxSize; i++) |
251 | 0 | { |
252 | 0 | char ch = pszVal[i]; |
253 | 0 | if (ch == '\0') |
254 | 0 | break; |
255 | 0 | int chVal; |
256 | 0 | if (ch >= 'A' && ch <= 'Z') |
257 | 0 | ch += 'a' - 'A'; |
258 | | /* i and o letters are excluded, */ |
259 | 0 | if (ch >= '0' && ch <= '9') |
260 | 0 | chVal = ch - '0'; |
261 | 0 | else if (ch >= 'a' && ch <= 'h') |
262 | 0 | chVal = ch - 'a' + 10; |
263 | 0 | else if (ch >= 'j' && ch <= 'n') |
264 | 0 | chVal = ch - 'a' + 10 - 1; |
265 | 0 | else if (ch >= 'p' && ch <= 'z') |
266 | 0 | chVal = ch - 'a' + 10 - 2; |
267 | 0 | else |
268 | 0 | { |
269 | 0 | CPLDebug("ECRG", "Invalid base34 value : %s", pszVal); |
270 | 0 | break; |
271 | 0 | } |
272 | 0 | nFrameNumber = nFrameNumber * 34 + chVal; |
273 | 0 | } |
274 | |
|
275 | 0 | return nFrameNumber; |
276 | 0 | } |
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 | | static int CEIL_ROUND(double a, double b) |
293 | 0 | { |
294 | 0 | return static_cast<int>(ceil(a / b) * b); |
295 | 0 | } |
296 | | |
297 | | static int NEAR_ROUND(double a, double b) |
298 | 0 | { |
299 | 0 | return static_cast<int>(floor((a / b) + 0.5) * b); |
300 | 0 | } |
301 | | |
302 | | constexpr int ECRG_PIXELS = 2304; |
303 | | |
304 | | 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 | 0 | { |
309 | 0 | const int nAbsZone = abs(nZone); |
310 | | #ifdef DEBUG |
311 | | 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 | 0 | const int nEW_ADRG = |
319 | 0 | CEIL_ROUND(anACst_ADRG[nAbsZone - 1] * (1e6 / nScale), 512); |
320 | 0 | const int nEW_CADRG = NEAR_ROUND(nEW_ADRG / (150. / 100.), 256); |
321 | | /* MIL-PRF-32283 - D.2.1.2 - East-west pixel constant. */ |
322 | 0 | 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 | 0 | const int nCols = |
329 | 0 | 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 | 0 | const int nNS_ADRG = CEIL_ROUND(nBCst_ADRG * (1e6 / nScale), 512) / 4; |
336 | 0 | 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 | 0 | const int nNS = nNS_CADRG / 256 * 384; |
340 | | |
341 | | /************************************************************************/ |
342 | | /* Compute number of latitudinal frames and latitude of top of zone */ |
343 | | /************************************************************************/ |
344 | 0 | dfPixelYSize = 90.0 / nNS; |
345 | |
|
346 | 0 | const double dfFrameLatHeight = dfPixelYSize * ECRG_PIXELS; |
347 | | |
348 | | /* MIL-PRF-32283 - D.2.1.5 - Equatorward and poleward zone extents. */ |
349 | 0 | int nUpperZoneFrames = |
350 | 0 | static_cast<int>(ceil(anZoneUpperLat[nAbsZone] / dfFrameLatHeight)); |
351 | 0 | int nBottomZoneFrames = static_cast<int>( |
352 | 0 | floor(anZoneUpperLat[nAbsZone - 1] / dfFrameLatHeight)); |
353 | 0 | const int nRows = nUpperZoneFrames - nBottomZoneFrames; |
354 | | |
355 | | /* Not sure to really understand D.2.1.5.a. Testing needed */ |
356 | 0 | if (nZone < 0) |
357 | 0 | { |
358 | 0 | nUpperZoneFrames = -nBottomZoneFrames; |
359 | | /*nBottomZoneFrames = nUpperZoneFrames - nRows;*/ |
360 | 0 | } |
361 | |
|
362 | 0 | 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 | 0 | const GIntBig nFrameNumber = GetFromBase34(pszFrameName, 10); |
370 | | |
371 | | /* MIL-PRF-32283 - A.2.6.1 */ |
372 | 0 | const GIntBig nY = nFrameNumber / nCols; |
373 | 0 | 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 | 0 | dfMaxY = dfUpperZoneTopLat - (nRows - 1 - nY) * dfFrameLatHeight; |
381 | 0 | dfMinY = dfMaxY - dfFrameLatHeight; |
382 | |
|
383 | 0 | dfPixelXSize = 360.0 / nEW; |
384 | |
|
385 | 0 | const double dfFrameLongWidth = dfPixelXSize * ECRG_PIXELS; |
386 | 0 | dfMinX = -180.0 + nX * dfFrameLongWidth; |
387 | 0 | 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 | 0 | } |
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 | | 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 | 0 | : m_nRasterXSize(nRasterXSize), m_nRasterYSize(nRasterYSize), |
417 | 0 | m_dfMinX(dfMinX), m_dfMaxY(dfMaxY), m_dfPixelXSize(dfPixelXSize), |
418 | 0 | m_dfPixelYSize(dfPixelYSize) |
419 | 0 | { |
420 | 0 | SetSrcBand(pszFilename, nBandIn); |
421 | 0 | SetSrcWindow(0, 0, nRasterXSize, nRasterYSize); |
422 | 0 | SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize); |
423 | 0 | } |
424 | | }; |
425 | | |
426 | | /************************************************************************/ |
427 | | /* ValidateOpenedBand() */ |
428 | | /************************************************************************/ |
429 | | |
430 | | #define WARN_CHECK_DS(x) \ |
431 | 0 | do \ |
432 | 0 | { \ |
433 | 0 | if (!(x)) \ |
434 | 0 | { \ |
435 | 0 | CPLError(CE_Warning, CPLE_AppDefined, \ |
436 | 0 | "For %s, assert '" #x "' failed", \ |
437 | 0 | poSourceDS->GetDescription()); \ |
438 | 0 | checkOK = false; \ |
439 | 0 | } \ |
440 | 0 | } while (false) |
441 | | |
442 | | bool ECRGTOCSource::ValidateOpenedBand(GDALRasterBand *poBand) const |
443 | 0 | { |
444 | 0 | bool checkOK = true; |
445 | 0 | auto poSourceDS = poBand->GetDataset(); |
446 | 0 | CPLAssert(poSourceDS); |
447 | |
|
448 | 0 | GDALGeoTransform l_gt; |
449 | 0 | poSourceDS->GetGeoTransform(l_gt); |
450 | 0 | WARN_CHECK_DS(fabs(l_gt.xorig - m_dfMinX) < 1e-10); |
451 | 0 | WARN_CHECK_DS(fabs(l_gt.yorig - m_dfMaxY) < 1e-10); |
452 | 0 | WARN_CHECK_DS(fabs(l_gt.xscale - m_dfPixelXSize) < 1e-10); |
453 | 0 | WARN_CHECK_DS(fabs(l_gt.yscale - (-m_dfPixelYSize)) < 1e-10); |
454 | 0 | WARN_CHECK_DS(l_gt.xrot == 0 && l_gt.yrot == 0); // No rotation. |
455 | 0 | WARN_CHECK_DS(poSourceDS->GetRasterCount() == 3); |
456 | 0 | WARN_CHECK_DS(poSourceDS->GetRasterXSize() == m_nRasterXSize); |
457 | 0 | WARN_CHECK_DS(poSourceDS->GetRasterYSize() == m_nRasterYSize); |
458 | 0 | WARN_CHECK_DS( |
459 | 0 | EQUAL(poSourceDS->GetProjectionRef(), SRS_WKT_WGS84_LAT_LONG)); |
460 | 0 | WARN_CHECK_DS(poSourceDS->GetRasterBand(1)->GetRasterDataType() == |
461 | 0 | GDT_UInt8); |
462 | 0 | return checkOK; |
463 | 0 | } |
464 | | |
465 | | /************************************************************************/ |
466 | | /* BuildFullName() */ |
467 | | /************************************************************************/ |
468 | | |
469 | | static std::string BuildFullName(const char *pszTOCFilename, |
470 | | const char *pszFramePath, |
471 | | const char *pszFrameName) |
472 | 0 | { |
473 | 0 | char *pszPath = nullptr; |
474 | 0 | if (pszFramePath[0] == '.' && |
475 | 0 | (pszFramePath[1] == '/' || pszFramePath[1] == '\\')) |
476 | 0 | pszPath = CPLStrdup(pszFramePath + 2); |
477 | 0 | else |
478 | 0 | pszPath = CPLStrdup(pszFramePath); |
479 | 0 | for (int i = 0; pszPath[i] != '\0'; i++) |
480 | 0 | { |
481 | 0 | if (pszPath[i] == '\\') |
482 | 0 | pszPath[i] = '/'; |
483 | 0 | } |
484 | 0 | const std::string osName = |
485 | 0 | CPLFormFilenameSafe(pszPath, pszFrameName, nullptr); |
486 | 0 | CPLFree(pszPath); |
487 | 0 | pszPath = nullptr; |
488 | 0 | std::string osTOCPath = CPLGetDirnameSafe(pszTOCFilename); |
489 | 0 | const auto nPosFirstSlashInName = osName.find('/'); |
490 | 0 | if (nPosFirstSlashInName != std::string::npos) |
491 | 0 | { |
492 | 0 | if (osTOCPath.size() >= nPosFirstSlashInName + 1 && |
493 | 0 | (osTOCPath[osTOCPath.size() - (nPosFirstSlashInName + 1)] == '/' || |
494 | 0 | osTOCPath[osTOCPath.size() - (nPosFirstSlashInName + 1)] == |
495 | 0 | '\\') && |
496 | 0 | strncmp(osTOCPath.c_str() + osTOCPath.size() - nPosFirstSlashInName, |
497 | 0 | osName.c_str(), nPosFirstSlashInName) == 0) |
498 | 0 | { |
499 | 0 | osTOCPath = CPLGetDirnameSafe(osTOCPath.c_str()); |
500 | 0 | } |
501 | 0 | } |
502 | 0 | return CPLProjectRelativeFilenameSafe(osTOCPath.c_str(), osName.c_str()); |
503 | 0 | } |
504 | | |
505 | | /************************************************************************/ |
506 | | /* Build() */ |
507 | | /************************************************************************/ |
508 | | |
509 | | /* Builds a ECRGTOCSubDataset from the set of files of the toc entry */ |
510 | | 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 | 0 | { |
517 | 0 | GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("VRT"); |
518 | 0 | if (poDriver == nullptr) |
519 | 0 | return nullptr; |
520 | | |
521 | 0 | const int nSizeX = static_cast<int>( |
522 | 0 | (dfGlobalMaxX - dfGlobalMinX) / dfGlobalPixelXSize + 0.5); |
523 | 0 | const int nSizeY = static_cast<int>( |
524 | 0 | (dfGlobalMaxY - dfGlobalMinY) / dfGlobalPixelYSize + 0.5); |
525 | | |
526 | | /* ------------------------------------ */ |
527 | | /* Create the VRT with the overall size */ |
528 | | /* ------------------------------------ */ |
529 | 0 | auto poVirtualDS = std::make_unique<ECRGTOCSubDataset>(nSizeX, nSizeY); |
530 | |
|
531 | 0 | poVirtualDS->SetProjection(SRS_WKT_WGS84_LAT_LONG); |
532 | |
|
533 | 0 | GDALGeoTransform gt{ |
534 | 0 | dfGlobalMinX, dfGlobalPixelXSize, 0, dfGlobalMaxY, 0, |
535 | 0 | -dfGlobalPixelYSize}; |
536 | 0 | poVirtualDS->SetGeoTransform(gt); |
537 | |
|
538 | 0 | for (int i = 0; i < 3; i++) |
539 | 0 | { |
540 | 0 | poVirtualDS->AddBand(GDT_UInt8, nullptr); |
541 | 0 | GDALRasterBand *poBand = poVirtualDS->GetRasterBand(i + 1); |
542 | 0 | poBand->SetColorInterpretation( |
543 | 0 | static_cast<GDALColorInterp>(GCI_RedBand + i)); |
544 | 0 | } |
545 | |
|
546 | 0 | poVirtualDS->SetDescription(pszTOCFilename); |
547 | |
|
548 | 0 | poVirtualDS->SetMetadataItem("PRODUCT_TITLE", pszProductTitle); |
549 | 0 | poVirtualDS->SetMetadataItem("DISC_ID", pszDiscId); |
550 | 0 | if (nScale != -1) |
551 | 0 | poVirtualDS->SetMetadataItem("SCALE", CPLString().Printf("%d", nScale)); |
552 | | |
553 | | /* -------------------------------------------------------------------- */ |
554 | | /* Check for overviews. */ |
555 | | /* -------------------------------------------------------------------- */ |
556 | |
|
557 | 0 | poVirtualDS->oOvManager.Initialize( |
558 | 0 | poVirtualDS.get(), |
559 | 0 | CPLString().Printf("%s.%d", pszTOCFilename, nCountSubDataset)); |
560 | |
|
561 | 0 | 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 | 0 | CPLConfigOptionSetter oSetter("GDAL_FORCE_CACHING", "NO", false); |
568 | |
|
569 | 0 | for (int i = 0; i < static_cast<int>(aosFrameDesc.size()); i++) |
570 | 0 | { |
571 | 0 | if (CPLHasPathTraversal(aosFrameDesc[i].pszName)) |
572 | 0 | { |
573 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
574 | 0 | "Path traversal detected in %s", aosFrameDesc[i].pszName); |
575 | 0 | return nullptr; |
576 | 0 | } |
577 | 0 | const std::string osName = BuildFullName( |
578 | 0 | pszTOCFilename, aosFrameDesc[i].pszPath, aosFrameDesc[i].pszName); |
579 | |
|
580 | 0 | double dfMinX = 0.0; |
581 | 0 | double dfMaxX = 0.0; |
582 | 0 | double dfMinY = 0.0; |
583 | 0 | double dfMaxY = 0.0; |
584 | 0 | double dfPixelXSize = 0.0; |
585 | 0 | double dfPixelYSize = 0.0; |
586 | 0 | ::GetExtent(aosFrameDesc[i].pszName, aosFrameDesc[i].nScale, |
587 | 0 | aosFrameDesc[i].nZone, dfMinX, dfMaxX, dfMinY, dfMaxY, |
588 | 0 | dfPixelXSize, dfPixelYSize); |
589 | |
|
590 | 0 | const int nFrameXSize = |
591 | 0 | static_cast<int>((dfMaxX - dfMinX) / dfPixelXSize + 0.5); |
592 | 0 | const int nFrameYSize = |
593 | 0 | static_cast<int>((dfMaxY - dfMinY) / dfPixelYSize + 0.5); |
594 | |
|
595 | 0 | poVirtualDS->papszFileList = |
596 | 0 | CSLAddString(poVirtualDS->papszFileList, osName.c_str()); |
597 | |
|
598 | 0 | for (int j = 0; j < 3; j++) |
599 | 0 | { |
600 | 0 | VRTSourcedRasterBand *poBand = |
601 | 0 | cpl::down_cast<VRTSourcedRasterBand *>( |
602 | 0 | poVirtualDS->GetRasterBand(j + 1)); |
603 | | /* Place the raster band at the right position in the VRT */ |
604 | 0 | auto poSource = new ECRGTOCSource( |
605 | 0 | osName.c_str(), j + 1, nFrameXSize, nFrameYSize, |
606 | 0 | static_cast<int>((dfMinX - dfGlobalMinX) / dfGlobalPixelXSize + |
607 | 0 | 0.5), |
608 | 0 | static_cast<int>((dfGlobalMaxY - dfMaxY) / dfGlobalPixelYSize + |
609 | 0 | 0.5), |
610 | 0 | static_cast<int>((dfMaxX - dfMinX) / dfGlobalPixelXSize + 0.5), |
611 | 0 | static_cast<int>((dfMaxY - dfMinY) / dfGlobalPixelYSize + 0.5), |
612 | 0 | dfMinX, dfMaxY, dfPixelXSize, dfPixelYSize); |
613 | 0 | poBand->AddSource(poSource); |
614 | 0 | } |
615 | 0 | } |
616 | | |
617 | 0 | poVirtualDS->SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL", |
618 | 0 | GDAL_MDD_IMAGE_STRUCTURE); |
619 | |
|
620 | 0 | return poVirtualDS.release(); |
621 | 0 | } |
622 | | |
623 | | /************************************************************************/ |
624 | | /* Build() */ |
625 | | /************************************************************************/ |
626 | | |
627 | | GDALDataset *ECRGTOCDataset::Build(const char *pszTOCFilename, |
628 | | CPLXMLNode *psXML, |
629 | | const std::string &osProduct, |
630 | | const std::string &osDiscId, |
631 | | const std::string &osScale, |
632 | | const char *pszOpenInfoFilename) |
633 | 24 | { |
634 | 24 | CPLXMLNode *psTOC = CPLGetXMLNode(psXML, "=Table_of_Contents"); |
635 | 24 | if (psTOC == nullptr) |
636 | 24 | { |
637 | 24 | CPLError(CE_Warning, CPLE_AppDefined, |
638 | 24 | "Cannot find Table_of_Contents element"); |
639 | 24 | return nullptr; |
640 | 24 | } |
641 | | |
642 | 0 | double dfGlobalMinX = 0.0; |
643 | 0 | double dfGlobalMinY = 0.0; |
644 | 0 | double dfGlobalMaxX = 0.0; |
645 | 0 | double dfGlobalMaxY = 0.0; |
646 | 0 | double dfGlobalPixelXSize = 0.0; |
647 | 0 | double dfGlobalPixelYSize = 0.0; |
648 | 0 | bool bGlobalExtentValid = false; |
649 | |
|
650 | 0 | auto poDS = std::make_unique<ECRGTOCDataset>(); |
651 | 0 | int nSubDatasets = 0; |
652 | |
|
653 | 0 | int bLookForSubDataset = !osProduct.empty() && !osDiscId.empty(); |
654 | |
|
655 | 0 | int nCountSubDataset = 0; |
656 | |
|
657 | 0 | poDS->SetDescription(pszOpenInfoFilename); |
658 | 0 | poDS->papszFileList = poDS->GDALDataset::GetFileList(); |
659 | |
|
660 | 0 | for (CPLXMLNode *psIter1 = psTOC->psChild; psIter1 != nullptr; |
661 | 0 | psIter1 = psIter1->psNext) |
662 | 0 | { |
663 | 0 | if (!(psIter1->eType == CXT_Element && psIter1->pszValue != nullptr && |
664 | 0 | strcmp(psIter1->pszValue, "product") == 0)) |
665 | 0 | continue; |
666 | | |
667 | 0 | const char *pszProductTitle = |
668 | 0 | CPLGetXMLValue(psIter1, "product_title", nullptr); |
669 | 0 | if (pszProductTitle == nullptr) |
670 | 0 | { |
671 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
672 | 0 | "Cannot find product_title attribute"); |
673 | 0 | continue; |
674 | 0 | } |
675 | | |
676 | 0 | if (bLookForSubDataset && |
677 | 0 | strcmp(LaunderString(pszProductTitle), osProduct.c_str()) != 0) |
678 | 0 | continue; |
679 | | |
680 | 0 | for (CPLXMLNode *psIter2 = psIter1->psChild; psIter2 != nullptr; |
681 | 0 | psIter2 = psIter2->psNext) |
682 | 0 | { |
683 | 0 | if (!(psIter2->eType == CXT_Element && |
684 | 0 | psIter2->pszValue != nullptr && |
685 | 0 | strcmp(psIter2->pszValue, "disc") == 0)) |
686 | 0 | continue; |
687 | | |
688 | 0 | const char *pszDiscId = CPLGetXMLValue(psIter2, "id", nullptr); |
689 | 0 | if (pszDiscId == nullptr) |
690 | 0 | { |
691 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
692 | 0 | "Cannot find id attribute"); |
693 | 0 | continue; |
694 | 0 | } |
695 | | |
696 | 0 | if (bLookForSubDataset && |
697 | 0 | strcmp(LaunderString(pszDiscId), osDiscId.c_str()) != 0) |
698 | 0 | continue; |
699 | | |
700 | 0 | CPLXMLNode *psFrameList = CPLGetXMLNode(psIter2, "frame_list"); |
701 | 0 | if (psFrameList == nullptr) |
702 | 0 | { |
703 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
704 | 0 | "Cannot find frame_list element"); |
705 | 0 | continue; |
706 | 0 | } |
707 | | |
708 | 0 | for (CPLXMLNode *psIter3 = psFrameList->psChild; psIter3 != nullptr; |
709 | 0 | psIter3 = psIter3->psNext) |
710 | 0 | { |
711 | 0 | if (!(psIter3->eType == CXT_Element && |
712 | 0 | psIter3->pszValue != nullptr && |
713 | 0 | strcmp(psIter3->pszValue, "scale") == 0)) |
714 | 0 | continue; |
715 | | |
716 | 0 | const char *pszSize = CPLGetXMLValue(psIter3, "size", nullptr); |
717 | 0 | if (pszSize == nullptr) |
718 | 0 | { |
719 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
720 | 0 | "Cannot find size attribute"); |
721 | 0 | continue; |
722 | 0 | } |
723 | | |
724 | 0 | int nScale = GetScaleFromString(pszSize); |
725 | 0 | if (nScale <= 0) |
726 | 0 | { |
727 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Invalid scale %s", |
728 | 0 | pszSize); |
729 | 0 | continue; |
730 | 0 | } |
731 | | |
732 | 0 | if (bLookForSubDataset) |
733 | 0 | { |
734 | 0 | if (!osScale.empty()) |
735 | 0 | { |
736 | 0 | if (strcmp(LaunderString(pszSize), osScale.c_str()) != |
737 | 0 | 0) |
738 | 0 | { |
739 | 0 | continue; |
740 | 0 | } |
741 | 0 | } |
742 | 0 | else |
743 | 0 | { |
744 | 0 | int nCountScales = 0; |
745 | 0 | for (CPLXMLNode *psIter4 = psFrameList->psChild; |
746 | 0 | psIter4 != nullptr; psIter4 = psIter4->psNext) |
747 | 0 | { |
748 | 0 | if (!(psIter4->eType == CXT_Element && |
749 | 0 | psIter4->pszValue != nullptr && |
750 | 0 | strcmp(psIter4->pszValue, "scale") == 0)) |
751 | 0 | continue; |
752 | 0 | nCountScales++; |
753 | 0 | } |
754 | 0 | if (nCountScales > 1) |
755 | 0 | { |
756 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
757 | 0 | "Scale should be mentioned in " |
758 | 0 | "subdatasets syntax since this disk " |
759 | 0 | "contains several scales"); |
760 | 0 | return nullptr; |
761 | 0 | } |
762 | 0 | } |
763 | 0 | } |
764 | | |
765 | 0 | nCountSubDataset++; |
766 | |
|
767 | 0 | std::vector<FrameDesc> aosFrameDesc; |
768 | 0 | int nValidFrames = 0; |
769 | |
|
770 | 0 | for (CPLXMLNode *psIter4 = psIter3->psChild; psIter4 != nullptr; |
771 | 0 | psIter4 = psIter4->psNext) |
772 | 0 | { |
773 | 0 | if (!(psIter4->eType == CXT_Element && |
774 | 0 | psIter4->pszValue != nullptr && |
775 | 0 | strcmp(psIter4->pszValue, "frame") == 0)) |
776 | 0 | continue; |
777 | | |
778 | 0 | const char *pszFrameName = |
779 | 0 | CPLGetXMLValue(psIter4, "name", nullptr); |
780 | 0 | if (pszFrameName == nullptr) |
781 | 0 | { |
782 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
783 | 0 | "Cannot find name element"); |
784 | 0 | continue; |
785 | 0 | } |
786 | | |
787 | 0 | if (strlen(pszFrameName) != 18) |
788 | 0 | { |
789 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
790 | 0 | "Invalid value for name element : %s", |
791 | 0 | pszFrameName); |
792 | 0 | continue; |
793 | 0 | } |
794 | | |
795 | 0 | const char *pszFramePath = |
796 | 0 | CPLGetXMLValue(psIter4, "frame_path", nullptr); |
797 | 0 | if (pszFramePath == nullptr) |
798 | 0 | { |
799 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
800 | 0 | "Cannot find frame_path element"); |
801 | 0 | continue; |
802 | 0 | } |
803 | | |
804 | 0 | const char *pszFrameZone = |
805 | 0 | CPLGetXMLValue(psIter4, "frame_zone", nullptr); |
806 | 0 | if (pszFrameZone == nullptr) |
807 | 0 | { |
808 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
809 | 0 | "Cannot find frame_zone element"); |
810 | 0 | continue; |
811 | 0 | } |
812 | 0 | if (strlen(pszFrameZone) != 1) |
813 | 0 | { |
814 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
815 | 0 | "Invalid value for frame_zone element : %s", |
816 | 0 | pszFrameZone); |
817 | 0 | continue; |
818 | 0 | } |
819 | 0 | char chZone = pszFrameZone[0]; |
820 | 0 | int nZone = 0; |
821 | 0 | if (chZone >= '1' && chZone <= '9') |
822 | 0 | nZone = chZone - '0'; |
823 | 0 | else if (chZone >= 'a' && chZone <= 'h') |
824 | 0 | nZone = -(chZone - 'a' + 1); |
825 | 0 | else if (chZone >= 'A' && chZone <= 'H') |
826 | 0 | nZone = -(chZone - 'A' + 1); |
827 | 0 | else if (chZone == 'j' || chZone == 'J') |
828 | 0 | nZone = -9; |
829 | 0 | else |
830 | 0 | { |
831 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
832 | 0 | "Invalid value for frame_zone element : %s", |
833 | 0 | pszFrameZone); |
834 | 0 | continue; |
835 | 0 | } |
836 | 0 | if (nZone == 9 || nZone == -9) |
837 | 0 | { |
838 | 0 | CPLError( |
839 | 0 | CE_Warning, CPLE_AppDefined, |
840 | 0 | "Polar zones unhandled by current implementation"); |
841 | 0 | continue; |
842 | 0 | } |
843 | | |
844 | 0 | double dfMinX = 0.0; |
845 | 0 | double dfMaxX = 0.0; |
846 | 0 | double dfMinY = 0.0; |
847 | 0 | double dfMaxY = 0.0; |
848 | 0 | double dfPixelXSize = 0.0; |
849 | 0 | double dfPixelYSize = 0.0; |
850 | 0 | ::GetExtent(pszFrameName, nScale, nZone, dfMinX, dfMaxX, |
851 | 0 | dfMinY, dfMaxY, dfPixelXSize, dfPixelYSize); |
852 | |
|
853 | 0 | nValidFrames++; |
854 | |
|
855 | 0 | if (CPLHasPathTraversal(pszFrameName)) |
856 | 0 | { |
857 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
858 | 0 | "Path traversal detected in %s", pszFrameName); |
859 | 0 | return nullptr; |
860 | 0 | } |
861 | 0 | const std::string osFullName = BuildFullName( |
862 | 0 | pszTOCFilename, pszFramePath, pszFrameName); |
863 | 0 | poDS->papszFileList = |
864 | 0 | CSLAddString(poDS->papszFileList, osFullName.c_str()); |
865 | |
|
866 | 0 | if (!bGlobalExtentValid) |
867 | 0 | { |
868 | 0 | dfGlobalMinX = dfMinX; |
869 | 0 | dfGlobalMinY = dfMinY; |
870 | 0 | dfGlobalMaxX = dfMaxX; |
871 | 0 | dfGlobalMaxY = dfMaxY; |
872 | 0 | dfGlobalPixelXSize = dfPixelXSize; |
873 | 0 | dfGlobalPixelYSize = dfPixelYSize; |
874 | 0 | bGlobalExtentValid = true; |
875 | 0 | } |
876 | 0 | else |
877 | 0 | { |
878 | 0 | if (dfMinX < dfGlobalMinX) |
879 | 0 | dfGlobalMinX = dfMinX; |
880 | 0 | if (dfMinY < dfGlobalMinY) |
881 | 0 | dfGlobalMinY = dfMinY; |
882 | 0 | if (dfMaxX > dfGlobalMaxX) |
883 | 0 | dfGlobalMaxX = dfMaxX; |
884 | 0 | if (dfMaxY > dfGlobalMaxY) |
885 | 0 | dfGlobalMaxY = dfMaxY; |
886 | 0 | if (dfPixelXSize < dfGlobalPixelXSize) |
887 | 0 | dfGlobalPixelXSize = dfPixelXSize; |
888 | 0 | if (dfPixelYSize < dfGlobalPixelYSize) |
889 | 0 | dfGlobalPixelYSize = dfPixelYSize; |
890 | 0 | } |
891 | |
|
892 | 0 | nValidFrames++; |
893 | |
|
894 | 0 | if (bLookForSubDataset) |
895 | 0 | { |
896 | 0 | FrameDesc frameDesc; |
897 | 0 | frameDesc.pszName = pszFrameName; |
898 | 0 | frameDesc.pszPath = pszFramePath; |
899 | 0 | frameDesc.nScale = nScale; |
900 | 0 | frameDesc.nZone = nZone; |
901 | 0 | aosFrameDesc.push_back(frameDesc); |
902 | 0 | } |
903 | 0 | } |
904 | | |
905 | 0 | if (bLookForSubDataset) |
906 | 0 | { |
907 | 0 | if (nValidFrames == 0) |
908 | 0 | return nullptr; |
909 | 0 | return ECRGTOCSubDataset::Build( |
910 | 0 | pszProductTitle, pszDiscId, nScale, nCountSubDataset, |
911 | 0 | pszTOCFilename, aosFrameDesc, dfGlobalMinX, |
912 | 0 | dfGlobalMinY, dfGlobalMaxX, dfGlobalMaxY, |
913 | 0 | dfGlobalPixelXSize, dfGlobalPixelYSize); |
914 | 0 | } |
915 | | |
916 | 0 | if (nValidFrames) |
917 | 0 | { |
918 | 0 | poDS->AddSubDataset(pszOpenInfoFilename, pszProductTitle, |
919 | 0 | pszDiscId, pszSize); |
920 | 0 | nSubDatasets++; |
921 | 0 | } |
922 | 0 | } |
923 | 0 | } |
924 | 0 | } |
925 | | |
926 | 0 | if (!bGlobalExtentValid) |
927 | 0 | { |
928 | 0 | return nullptr; |
929 | 0 | } |
930 | | |
931 | 0 | if (nSubDatasets == 1) |
932 | 0 | { |
933 | 0 | const char *pszSubDatasetName = CSLFetchNameValue( |
934 | 0 | poDS->GetMetadata(GDAL_MDD_SUBDATASETS), "SUBDATASET_1_NAME"); |
935 | 0 | GDALOpenInfo oOpenInfo(pszSubDatasetName, GA_ReadOnly); |
936 | 0 | poDS.reset(); |
937 | 0 | GDALDataset *poRetDS = Open(&oOpenInfo); |
938 | 0 | if (poRetDS) |
939 | 0 | poRetDS->SetDescription(pszOpenInfoFilename); |
940 | 0 | return poRetDS; |
941 | 0 | } |
942 | | |
943 | 0 | poDS->m_gt.xorig = dfGlobalMinX; |
944 | 0 | poDS->m_gt.xscale = dfGlobalPixelXSize; |
945 | 0 | poDS->m_gt.xrot = 0.0; |
946 | 0 | poDS->m_gt.yorig = dfGlobalMaxY; |
947 | 0 | poDS->m_gt.yrot = 0.0; |
948 | 0 | poDS->m_gt.yscale = -dfGlobalPixelYSize; |
949 | |
|
950 | 0 | poDS->nRasterXSize = static_cast<int>(0.5 + (dfGlobalMaxX - dfGlobalMinX) / |
951 | 0 | dfGlobalPixelXSize); |
952 | 0 | poDS->nRasterYSize = static_cast<int>(0.5 + (dfGlobalMaxY - dfGlobalMinY) / |
953 | 0 | dfGlobalPixelYSize); |
954 | | |
955 | | /* -------------------------------------------------------------------- */ |
956 | | /* Initialize any PAM information. */ |
957 | | /* -------------------------------------------------------------------- */ |
958 | 0 | poDS->TryLoadXML(); |
959 | |
|
960 | 0 | return poDS.release(); |
961 | 0 | } |
962 | | |
963 | | /************************************************************************/ |
964 | | /* Open() */ |
965 | | /************************************************************************/ |
966 | | |
967 | | GDALDataset *ECRGTOCDataset::Open(GDALOpenInfo *poOpenInfo) |
968 | | |
969 | 544 | { |
970 | 544 | if (!ECRGTOCDriverIdentify(poOpenInfo)) |
971 | 0 | return nullptr; |
972 | | |
973 | 544 | const char *pszFilename = poOpenInfo->pszFilename; |
974 | 544 | CPLString osFilename; |
975 | 544 | CPLString osProduct, osDiscId, osScale; |
976 | | |
977 | 544 | if (STARTS_WITH_CI(pszFilename, "ECRG_TOC_ENTRY:")) |
978 | 0 | { |
979 | 0 | pszFilename += strlen("ECRG_TOC_ENTRY:"); |
980 | | |
981 | | /* PRODUCT:DISK:SCALE:FILENAME (or PRODUCT:DISK:FILENAME historically) |
982 | | */ |
983 | | /* with FILENAME potentially C:\BLA... */ |
984 | 0 | const CPLStringList aosTokens(CSLTokenizeString2(pszFilename, ":", 0)); |
985 | 0 | if (aosTokens.size() == 3) |
986 | 0 | osFilename = aosTokens[2]; |
987 | 0 | else if (aosTokens.size() == 4) |
988 | 0 | { |
989 | 0 | if (strlen(aosTokens[2]) == 1 && |
990 | 0 | (aosTokens[3][0] == '\\' || aosTokens[3][0] == '/')) |
991 | 0 | { |
992 | 0 | osFilename = aosTokens[2]; |
993 | 0 | osFilename += ":"; |
994 | 0 | osFilename += aosTokens[3]; |
995 | 0 | } |
996 | 0 | else |
997 | 0 | { |
998 | 0 | osScale = aosTokens[2]; |
999 | 0 | osFilename = aosTokens[3]; |
1000 | 0 | } |
1001 | 0 | } |
1002 | 0 | else if (aosTokens.size() == 5 && strlen(aosTokens[3]) == 1 && |
1003 | 0 | (aosTokens[4][0] == '\\' || aosTokens[4][0] == '/')) |
1004 | 0 | { |
1005 | 0 | osScale = aosTokens[2]; |
1006 | 0 | osFilename = aosTokens[3]; |
1007 | 0 | osFilename += ":"; |
1008 | 0 | osFilename += aosTokens[4]; |
1009 | 0 | } |
1010 | 0 | else |
1011 | 0 | { |
1012 | 0 | return nullptr; |
1013 | 0 | } |
1014 | | |
1015 | 0 | osProduct = aosTokens[0]; |
1016 | 0 | osDiscId = aosTokens[1]; |
1017 | |
|
1018 | 0 | pszFilename = osFilename.c_str(); |
1019 | 0 | } |
1020 | | |
1021 | | /* -------------------------------------------------------------------- */ |
1022 | | /* Parse the XML file */ |
1023 | | /* -------------------------------------------------------------------- */ |
1024 | 544 | CPLXMLNode *psXML = CPLParseXMLFile(pszFilename); |
1025 | 544 | if (psXML == nullptr) |
1026 | 520 | { |
1027 | 520 | return nullptr; |
1028 | 520 | } |
1029 | | |
1030 | 24 | GDALDataset *poDS = Build(pszFilename, psXML, osProduct, osDiscId, osScale, |
1031 | 24 | poOpenInfo->pszFilename); |
1032 | 24 | CPLDestroyXMLNode(psXML); |
1033 | | |
1034 | 24 | if (poDS && poOpenInfo->eAccess == GA_Update) |
1035 | 0 | { |
1036 | 0 | ReportUpdateNotSupportedByDriver("ECRGTOC"); |
1037 | 0 | delete poDS; |
1038 | 0 | return nullptr; |
1039 | 0 | } |
1040 | | |
1041 | 24 | return poDS; |
1042 | 24 | } |
1043 | | |
1044 | | /************************************************************************/ |
1045 | | /* GDALRegister_ECRGTOC() */ |
1046 | | /************************************************************************/ |
1047 | | |
1048 | | void GDALRegister_ECRGTOC() |
1049 | | |
1050 | 24 | { |
1051 | 24 | if (GDALGetDriverByName(ECRGTOC_DRIVER_NAME) != nullptr) |
1052 | 0 | return; |
1053 | | |
1054 | 24 | GDALDriver *poDriver = new GDALDriver(); |
1055 | 24 | ECRGTOCDriverSetCommonMetadata(poDriver); |
1056 | | |
1057 | 24 | poDriver->pfnOpen = ECRGTOCDataset::Open; |
1058 | | |
1059 | 24 | GetGDALDriverManager()->RegisterDriver(poDriver); |
1060 | 24 | } |