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