/src/gdal/frmts/raw/pauxdataset.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: PCI .aux Driver |
4 | | * Purpose: Implementation of PAuxDataset |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 1999, Frank Warmerdam |
9 | | * Copyright (c) 2008-2010, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_string.h" |
15 | | #include "gdal_frmts.h" |
16 | | #include "gdal_priv.h" |
17 | | #include "ogr_spatialref.h" |
18 | | #include "rawdataset.h" |
19 | | |
20 | | #include <cmath> |
21 | | |
22 | | /************************************************************************/ |
23 | | /* ==================================================================== */ |
24 | | /* PAuxDataset */ |
25 | | /* ==================================================================== */ |
26 | | /************************************************************************/ |
27 | | |
28 | | class PAuxRasterBand; |
29 | | |
30 | | class PAuxDataset final : public RawDataset |
31 | | { |
32 | | friend class PAuxRasterBand; |
33 | | |
34 | | VSILFILE *fpImage; // Image data file. |
35 | | |
36 | | int nGCPCount; |
37 | | GDAL_GCP *pasGCPList; |
38 | | OGRSpatialReference m_oGCPSRS{}; |
39 | | |
40 | | void ScanForGCPs(); |
41 | | static OGRSpatialReference PCI2SRS(const char *pszGeosys, |
42 | | const char *pszProjParams); |
43 | | |
44 | | OGRSpatialReference m_oSRS{}; |
45 | | |
46 | | CPL_DISALLOW_COPY_ASSIGN(PAuxDataset) |
47 | | |
48 | | CPLErr Close() override; |
49 | | |
50 | | public: |
51 | | PAuxDataset(); |
52 | | ~PAuxDataset() override; |
53 | | |
54 | | // TODO(schwehr): Why are these public? |
55 | | char *pszAuxFilename; |
56 | | char **papszAuxLines; |
57 | | |
58 | | const OGRSpatialReference *GetSpatialRef() const override |
59 | 0 | { |
60 | 0 | return m_oSRS.IsEmpty() ? nullptr : &m_oSRS; |
61 | 0 | } |
62 | | |
63 | | CPLErr GetGeoTransform(GDALGeoTransform >) const override; |
64 | | |
65 | | int GetGCPCount() override; |
66 | | |
67 | | const OGRSpatialReference *GetGCPSpatialRef() const override |
68 | 0 | { |
69 | 0 | return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS; |
70 | 0 | } |
71 | | |
72 | | const GDAL_GCP *GetGCPs() override; |
73 | | |
74 | | char **GetFileList() override; |
75 | | |
76 | | static GDALDataset *Open(GDALOpenInfo *); |
77 | | }; |
78 | | |
79 | | /************************************************************************/ |
80 | | /* ==================================================================== */ |
81 | | /* PAuxRasterBand */ |
82 | | /* ==================================================================== */ |
83 | | /************************************************************************/ |
84 | | |
85 | | class PAuxRasterBand final : public RawRasterBand |
86 | | { |
87 | | CPL_DISALLOW_COPY_ASSIGN(PAuxRasterBand) |
88 | | |
89 | | public: |
90 | | PAuxRasterBand(GDALDataset *poDS, int nBand, VSILFILE *fpRaw, |
91 | | vsi_l_offset nImgOffset, int nPixelOffset, int nLineOffset, |
92 | | GDALDataType eDataType, int bNativeOrder); |
93 | | |
94 | | ~PAuxRasterBand() override; |
95 | | |
96 | | double GetNoDataValue(int *pbSuccess = nullptr) override; |
97 | | |
98 | | GDALColorTable *GetColorTable() override; |
99 | | GDALColorInterp GetColorInterpretation() override; |
100 | | }; |
101 | | |
102 | | /************************************************************************/ |
103 | | /* PAuxRasterBand() */ |
104 | | /************************************************************************/ |
105 | | |
106 | | PAuxRasterBand::PAuxRasterBand(GDALDataset *poDSIn, int nBandIn, |
107 | | VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn, |
108 | | int nPixelOffsetIn, int nLineOffsetIn, |
109 | | GDALDataType eDataTypeIn, int bNativeOrderIn) |
110 | 0 | : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn, |
111 | 0 | nLineOffsetIn, eDataTypeIn, bNativeOrderIn, |
112 | 0 | RawRasterBand::OwnFP::NO) |
113 | 0 | { |
114 | 0 | PAuxDataset *poPDS = cpl::down_cast<PAuxDataset *>(poDS); |
115 | | |
116 | | /* -------------------------------------------------------------------- */ |
117 | | /* Does this channel have a description? */ |
118 | | /* -------------------------------------------------------------------- */ |
119 | 0 | char szTarget[128] = {'\0'}; |
120 | |
|
121 | 0 | snprintf(szTarget, sizeof(szTarget), "ChanDesc-%d", nBand); |
122 | 0 | if (CSLFetchNameValue(poPDS->papszAuxLines, szTarget) != nullptr) |
123 | 0 | GDALRasterBand::SetDescription( |
124 | 0 | CSLFetchNameValue(poPDS->papszAuxLines, szTarget)); |
125 | | |
126 | | /* -------------------------------------------------------------------- */ |
127 | | /* See if we have colors. Currently we must have color zero, */ |
128 | | /* but this should not really be a limitation. */ |
129 | | /* -------------------------------------------------------------------- */ |
130 | 0 | snprintf(szTarget, sizeof(szTarget), "METADATA_IMG_%d_Class_%d_Color", |
131 | 0 | nBand, 0); |
132 | 0 | if (CSLFetchNameValue(poPDS->papszAuxLines, szTarget) != nullptr) |
133 | 0 | { |
134 | 0 | poCT = new GDALColorTable(); |
135 | |
|
136 | 0 | for (int i = 0; i < 256; i++) |
137 | 0 | { |
138 | 0 | snprintf(szTarget, sizeof(szTarget), |
139 | 0 | "METADATA_IMG_%d_Class_%d_Color", nBand, i); |
140 | 0 | const char *pszLine = |
141 | 0 | CSLFetchNameValue(poPDS->papszAuxLines, szTarget); |
142 | 0 | while (pszLine && *pszLine == ' ') |
143 | 0 | pszLine++; |
144 | |
|
145 | 0 | int nRed = 0; |
146 | 0 | int nGreen = 0; |
147 | 0 | int nBlue = 0; |
148 | | // TODO(schwehr): Replace sscanf with something safe. |
149 | 0 | if (pszLine != nullptr && STARTS_WITH_CI(pszLine, "(RGB:") && |
150 | 0 | sscanf(pszLine + 5, "%d %d %d", &nRed, &nGreen, &nBlue) == 3) |
151 | 0 | { |
152 | 0 | GDALColorEntry oColor = {static_cast<short>(nRed), |
153 | 0 | static_cast<short>(nGreen), |
154 | 0 | static_cast<short>(nBlue), 255}; |
155 | |
|
156 | 0 | poCT->SetColorEntry(i, &oColor); |
157 | 0 | } |
158 | 0 | } |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | | /************************************************************************/ |
163 | | /* ~PAuxRasterBand() */ |
164 | | /************************************************************************/ |
165 | | |
166 | | PAuxRasterBand::~PAuxRasterBand() |
167 | | |
168 | 0 | { |
169 | 0 | } |
170 | | |
171 | | /************************************************************************/ |
172 | | /* GetNoDataValue() */ |
173 | | /************************************************************************/ |
174 | | |
175 | | double PAuxRasterBand::GetNoDataValue(int *pbSuccess) |
176 | | |
177 | 0 | { |
178 | 0 | char szTarget[128] = {'\0'}; |
179 | 0 | snprintf(szTarget, sizeof(szTarget), "METADATA_IMG_%d_NO_DATA_VALUE", |
180 | 0 | nBand); |
181 | |
|
182 | 0 | PAuxDataset *poPDS = cpl::down_cast<PAuxDataset *>(poDS); |
183 | 0 | const char *pszLine = CSLFetchNameValue(poPDS->papszAuxLines, szTarget); |
184 | |
|
185 | 0 | if (pbSuccess != nullptr) |
186 | 0 | *pbSuccess = (pszLine != nullptr); |
187 | |
|
188 | 0 | if (pszLine == nullptr) |
189 | 0 | return -1.0e8; |
190 | | |
191 | 0 | return CPLAtof(pszLine); |
192 | 0 | } |
193 | | |
194 | | /************************************************************************/ |
195 | | /* GetColorTable() */ |
196 | | /************************************************************************/ |
197 | | |
198 | | GDALColorTable *PAuxRasterBand::GetColorTable() |
199 | | |
200 | 0 | { |
201 | 0 | return poCT; |
202 | 0 | } |
203 | | |
204 | | /************************************************************************/ |
205 | | /* GetColorInterpretation() */ |
206 | | /************************************************************************/ |
207 | | |
208 | | GDALColorInterp PAuxRasterBand::GetColorInterpretation() |
209 | | |
210 | 0 | { |
211 | 0 | if (poCT == nullptr) |
212 | 0 | return GCI_Undefined; |
213 | | |
214 | 0 | return GCI_PaletteIndex; |
215 | 0 | } |
216 | | |
217 | | /************************************************************************/ |
218 | | /* ==================================================================== */ |
219 | | /* PAuxDataset */ |
220 | | /* ==================================================================== */ |
221 | | /************************************************************************/ |
222 | | |
223 | | /************************************************************************/ |
224 | | /* PAuxDataset() */ |
225 | | /************************************************************************/ |
226 | | |
227 | | PAuxDataset::PAuxDataset() |
228 | 1 | : fpImage(nullptr), nGCPCount(0), pasGCPList(nullptr), |
229 | 1 | pszAuxFilename(nullptr), papszAuxLines(nullptr) |
230 | 1 | { |
231 | 1 | m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
232 | 1 | m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
233 | 1 | } |
234 | | |
235 | | /************************************************************************/ |
236 | | /* ~PAuxDataset() */ |
237 | | /************************************************************************/ |
238 | | |
239 | | PAuxDataset::~PAuxDataset() |
240 | | |
241 | 1 | { |
242 | 1 | PAuxDataset::Close(); |
243 | 1 | } |
244 | | |
245 | | /************************************************************************/ |
246 | | /* Close() */ |
247 | | /************************************************************************/ |
248 | | |
249 | | CPLErr PAuxDataset::Close() |
250 | 1 | { |
251 | 1 | CPLErr eErr = CE_None; |
252 | 1 | if (nOpenFlags != OPEN_FLAGS_CLOSED) |
253 | 1 | { |
254 | 1 | if (PAuxDataset::FlushCache(true) != CE_None) |
255 | 0 | eErr = CE_Failure; |
256 | | |
257 | 1 | if (fpImage != nullptr) |
258 | 0 | { |
259 | 0 | if (VSIFCloseL(fpImage) != 0) |
260 | 0 | { |
261 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
262 | 0 | eErr = CE_Failure; |
263 | 0 | } |
264 | 0 | } |
265 | | |
266 | 1 | GDALDeinitGCPs(nGCPCount, pasGCPList); |
267 | 1 | CPLFree(pasGCPList); |
268 | | |
269 | 1 | CPLFree(pszAuxFilename); |
270 | 1 | CSLDestroy(papszAuxLines); |
271 | | |
272 | 1 | if (GDALPamDataset::Close() != CE_None) |
273 | 0 | eErr = CE_Failure; |
274 | 1 | } |
275 | 1 | return eErr; |
276 | 1 | } |
277 | | |
278 | | /************************************************************************/ |
279 | | /* GetFileList() */ |
280 | | /************************************************************************/ |
281 | | |
282 | | char **PAuxDataset::GetFileList() |
283 | | |
284 | 0 | { |
285 | 0 | char **papszFileList = RawDataset::GetFileList(); |
286 | 0 | papszFileList = CSLAddString(papszFileList, pszAuxFilename); |
287 | 0 | return papszFileList; |
288 | 0 | } |
289 | | |
290 | | /************************************************************************/ |
291 | | /* PCI2SRS() */ |
292 | | /* */ |
293 | | /* Convert PCI coordinate system to WKT. For now this is very */ |
294 | | /* incomplete, but can be filled out in the future. */ |
295 | | /************************************************************************/ |
296 | | |
297 | | OGRSpatialReference PAuxDataset::PCI2SRS(const char *pszGeosys, |
298 | | const char *pszProjParams) |
299 | | |
300 | 0 | { |
301 | 0 | while (*pszGeosys == ' ') |
302 | 0 | pszGeosys++; |
303 | | |
304 | | /* -------------------------------------------------------------------- */ |
305 | | /* Parse projection parameters array. */ |
306 | | /* -------------------------------------------------------------------- */ |
307 | 0 | double adfProjParams[16] = {0.0}; |
308 | |
|
309 | 0 | if (pszProjParams != nullptr) |
310 | 0 | { |
311 | 0 | char **papszTokens = CSLTokenizeString(pszProjParams); |
312 | |
|
313 | 0 | for (int i = 0; |
314 | 0 | i < 16 && papszTokens != nullptr && papszTokens[i] != nullptr; i++) |
315 | 0 | adfProjParams[i] = CPLAtof(papszTokens[i]); |
316 | |
|
317 | 0 | CSLDestroy(papszTokens); |
318 | 0 | } |
319 | | |
320 | | /* -------------------------------------------------------------------- */ |
321 | | /* Convert to SRS. */ |
322 | | /* -------------------------------------------------------------------- */ |
323 | 0 | OGRSpatialReference oSRS; |
324 | 0 | oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
325 | 0 | if (oSRS.importFromPCI(pszGeosys, nullptr, adfProjParams) != OGRERR_NONE) |
326 | 0 | { |
327 | 0 | oSRS.Clear(); |
328 | 0 | } |
329 | |
|
330 | 0 | return oSRS; |
331 | 0 | } |
332 | | |
333 | | /************************************************************************/ |
334 | | /* ScanForGCPs() */ |
335 | | /************************************************************************/ |
336 | | |
337 | | void PAuxDataset::ScanForGCPs() |
338 | | |
339 | 0 | { |
340 | 0 | const int MAX_GCP = 256; |
341 | |
|
342 | 0 | nGCPCount = 0; |
343 | 0 | CPLAssert(pasGCPList == nullptr); |
344 | 0 | pasGCPList = static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), MAX_GCP)); |
345 | | |
346 | | /* -------------------------------------------------------------------- */ |
347 | | /* Get the GCP coordinate system. */ |
348 | | /* -------------------------------------------------------------------- */ |
349 | 0 | const char *pszMapUnits = |
350 | 0 | CSLFetchNameValue(papszAuxLines, "GCP_1_MapUnits"); |
351 | 0 | const char *pszProjParams = |
352 | 0 | CSLFetchNameValue(papszAuxLines, "GCP_1_ProjParms"); |
353 | |
|
354 | 0 | if (pszMapUnits != nullptr) |
355 | 0 | m_oGCPSRS = PCI2SRS(pszMapUnits, pszProjParams); |
356 | | |
357 | | /* -------------------------------------------------------------------- */ |
358 | | /* Collect standalone GCPs. They look like: */ |
359 | | /* */ |
360 | | /* GCP_1_n = row, col, x, y [,z [,"id"[, "desc"]]] */ |
361 | | /* -------------------------------------------------------------------- */ |
362 | 0 | for (int i = 0; nGCPCount < MAX_GCP; i++) |
363 | 0 | { |
364 | 0 | char szName[50] = {'\0'}; |
365 | 0 | snprintf(szName, sizeof(szName), "GCP_1_%d", i + 1); |
366 | 0 | if (CSLFetchNameValue(papszAuxLines, szName) == nullptr) |
367 | 0 | break; |
368 | | |
369 | 0 | char **papszTokens = CSLTokenizeStringComplex( |
370 | 0 | CSLFetchNameValue(papszAuxLines, szName), " ", TRUE, FALSE); |
371 | |
|
372 | 0 | if (CSLCount(papszTokens) >= 4) |
373 | 0 | { |
374 | 0 | GDALInitGCPs(1, pasGCPList + nGCPCount); |
375 | |
|
376 | 0 | pasGCPList[nGCPCount].dfGCPX = CPLAtof(papszTokens[2]); |
377 | 0 | pasGCPList[nGCPCount].dfGCPY = CPLAtof(papszTokens[3]); |
378 | 0 | pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(papszTokens[0]); |
379 | 0 | pasGCPList[nGCPCount].dfGCPLine = CPLAtof(papszTokens[1]); |
380 | |
|
381 | 0 | if (CSLCount(papszTokens) > 4) |
382 | 0 | pasGCPList[nGCPCount].dfGCPZ = CPLAtof(papszTokens[4]); |
383 | |
|
384 | 0 | CPLFree(pasGCPList[nGCPCount].pszId); |
385 | 0 | if (CSLCount(papszTokens) > 5) |
386 | 0 | { |
387 | 0 | pasGCPList[nGCPCount].pszId = CPLStrdup(papszTokens[5]); |
388 | 0 | } |
389 | 0 | else |
390 | 0 | { |
391 | 0 | snprintf(szName, sizeof(szName), "GCP_%d", i + 1); |
392 | 0 | pasGCPList[nGCPCount].pszId = CPLStrdup(szName); |
393 | 0 | } |
394 | |
|
395 | 0 | if (CSLCount(papszTokens) > 6) |
396 | 0 | { |
397 | 0 | CPLFree(pasGCPList[nGCPCount].pszInfo); |
398 | 0 | pasGCPList[nGCPCount].pszInfo = CPLStrdup(papszTokens[6]); |
399 | 0 | } |
400 | |
|
401 | 0 | nGCPCount++; |
402 | 0 | } |
403 | |
|
404 | 0 | CSLDestroy(papszTokens); |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | /************************************************************************/ |
409 | | /* GetGCPCount() */ |
410 | | /************************************************************************/ |
411 | | |
412 | | int PAuxDataset::GetGCPCount() |
413 | | |
414 | 0 | { |
415 | 0 | return nGCPCount; |
416 | 0 | } |
417 | | |
418 | | /************************************************************************/ |
419 | | /* GetGCP() */ |
420 | | /************************************************************************/ |
421 | | |
422 | | const GDAL_GCP *PAuxDataset::GetGCPs() |
423 | | |
424 | 0 | { |
425 | 0 | return pasGCPList; |
426 | 0 | } |
427 | | |
428 | | /************************************************************************/ |
429 | | /* GetGeoTransform() */ |
430 | | /************************************************************************/ |
431 | | |
432 | | CPLErr PAuxDataset::GetGeoTransform(GDALGeoTransform >) const |
433 | | |
434 | 0 | { |
435 | 0 | if (CSLFetchNameValue(papszAuxLines, "UpLeftX") == nullptr || |
436 | 0 | CSLFetchNameValue(papszAuxLines, "UpLeftY") == nullptr || |
437 | 0 | CSLFetchNameValue(papszAuxLines, "LoRightX") == nullptr || |
438 | 0 | CSLFetchNameValue(papszAuxLines, "LoRightY") == nullptr) |
439 | 0 | { |
440 | 0 | gt = GDALGeoTransform(); |
441 | 0 | return CE_Failure; |
442 | 0 | } |
443 | | |
444 | 0 | const double dfUpLeftX = |
445 | 0 | CPLAtof(CSLFetchNameValue(papszAuxLines, "UpLeftX")); |
446 | 0 | const double dfUpLeftY = |
447 | 0 | CPLAtof(CSLFetchNameValue(papszAuxLines, "UpLeftY")); |
448 | 0 | const double dfLoRightX = |
449 | 0 | CPLAtof(CSLFetchNameValue(papszAuxLines, "LoRightX")); |
450 | 0 | const double dfLoRightY = |
451 | 0 | CPLAtof(CSLFetchNameValue(papszAuxLines, "LoRightY")); |
452 | |
|
453 | 0 | gt[0] = dfUpLeftX; |
454 | 0 | gt[1] = (dfLoRightX - dfUpLeftX) / GetRasterXSize(); |
455 | 0 | gt[2] = 0.0; |
456 | 0 | gt[3] = dfUpLeftY; |
457 | 0 | gt[4] = 0.0; |
458 | 0 | gt[5] = (dfLoRightY - dfUpLeftY) / GetRasterYSize(); |
459 | |
|
460 | 0 | return CE_None; |
461 | 0 | } |
462 | | |
463 | | /************************************************************************/ |
464 | | /* Open() */ |
465 | | /************************************************************************/ |
466 | | |
467 | | GDALDataset *PAuxDataset::Open(GDALOpenInfo *poOpenInfo) |
468 | | |
469 | 597k | { |
470 | 597k | if (poOpenInfo->nHeaderBytes < 1 || |
471 | 49.2k | (!poOpenInfo->IsSingleAllowedDriver("PAux") && |
472 | 49.2k | poOpenInfo->IsExtensionEqualToCI("zarr"))) |
473 | 548k | { |
474 | 548k | return nullptr; |
475 | 548k | } |
476 | | |
477 | | /* -------------------------------------------------------------------- */ |
478 | | /* If this is an .aux file, fetch out and form the name of the */ |
479 | | /* file it references. */ |
480 | | /* -------------------------------------------------------------------- */ |
481 | | |
482 | 49.2k | CPLString osTarget = poOpenInfo->pszFilename; |
483 | | |
484 | 49.2k | if (poOpenInfo->IsExtensionEqualToCI("aux") && |
485 | 8.92k | STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader), |
486 | 49.2k | "AuxilaryTarget: ")) |
487 | 0 | { |
488 | 0 | const char *pszSrc = |
489 | 0 | reinterpret_cast<const char *>(poOpenInfo->pabyHeader + 16); |
490 | |
|
491 | 0 | char szAuxTarget[1024] = {'\0'}; |
492 | 0 | for (int i = 0; i < static_cast<int>(sizeof(szAuxTarget)) - 1 && |
493 | 0 | pszSrc[i] != 10 && pszSrc[i] != 13 && pszSrc[i] != '\0'; |
494 | 0 | i++) |
495 | 0 | { |
496 | 0 | szAuxTarget[i] = pszSrc[i]; |
497 | 0 | } |
498 | 0 | szAuxTarget[sizeof(szAuxTarget) - 1] = '\0'; |
499 | |
|
500 | 0 | if (CPLHasPathTraversal(szAuxTarget)) |
501 | 0 | { |
502 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
503 | 0 | "Path traversal detected in %s", szAuxTarget); |
504 | 0 | return nullptr; |
505 | 0 | } |
506 | 0 | const std::string osPath(CPLGetPathSafe(poOpenInfo->pszFilename)); |
507 | 0 | osTarget = CPLFormFilenameSafe(osPath.c_str(), szAuxTarget, nullptr); |
508 | 0 | } |
509 | | |
510 | | /* -------------------------------------------------------------------- */ |
511 | | /* Now we need to tear apart the filename to form a .aux */ |
512 | | /* filename. */ |
513 | | /* -------------------------------------------------------------------- */ |
514 | 49.2k | CPLString osAuxFilename = CPLResetExtensionSafe(osTarget, "aux"); |
515 | | |
516 | | /* -------------------------------------------------------------------- */ |
517 | | /* Do we have a .aux file? */ |
518 | | /* -------------------------------------------------------------------- */ |
519 | 49.2k | CSLConstList papszSiblingFiles = poOpenInfo->GetSiblingFiles(); |
520 | 49.2k | if (papszSiblingFiles != nullptr && |
521 | 41.8k | CSLFindString(papszSiblingFiles, CPLGetFilename(osAuxFilename)) == -1) |
522 | 32.2k | { |
523 | 32.2k | return nullptr; |
524 | 32.2k | } |
525 | | |
526 | 16.9k | VSILFILE *fp = VSIFOpenL(osAuxFilename, "r"); |
527 | 16.9k | if (fp == nullptr) |
528 | 8.31k | { |
529 | 8.31k | osAuxFilename = CPLResetExtensionSafe(osTarget, "AUX"); |
530 | 8.31k | fp = VSIFOpenL(osAuxFilename, "r"); |
531 | 8.31k | } |
532 | | |
533 | 16.9k | if (fp == nullptr) |
534 | 7.43k | return nullptr; |
535 | | |
536 | | /* -------------------------------------------------------------------- */ |
537 | | /* Is this file a PCI .aux file? Check the first line for the */ |
538 | | /* telltale AuxilaryTarget keyword. */ |
539 | | /* */ |
540 | | /* At this point we should be verifying that it refers to our */ |
541 | | /* binary file, but that is a pretty involved test. */ |
542 | | /* -------------------------------------------------------------------- */ |
543 | 9.56k | CPLPushErrorHandler(CPLQuietErrorHandler); |
544 | 9.56k | const char *pszLine = CPLReadLine2L(fp, 1024, nullptr); |
545 | 9.56k | CPLPopErrorHandler(); |
546 | | |
547 | 9.56k | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
548 | | |
549 | 9.56k | if (pszLine == nullptr || (!STARTS_WITH_CI(pszLine, "AuxilaryTarget") && |
550 | 9.24k | !STARTS_WITH_CI(pszLine, "AuxiliaryTarget"))) |
551 | 9.56k | { |
552 | 9.56k | CPLErrorReset(); |
553 | 9.56k | return nullptr; |
554 | 9.56k | } |
555 | | |
556 | | /* -------------------------------------------------------------------- */ |
557 | | /* Create a corresponding GDALDataset. */ |
558 | | /* -------------------------------------------------------------------- */ |
559 | 1 | auto poDS = std::make_unique<PAuxDataset>(); |
560 | | |
561 | | /* -------------------------------------------------------------------- */ |
562 | | /* Load the .aux file into a string list suitable to be */ |
563 | | /* searched with CSLFetchNameValue(). */ |
564 | | /* -------------------------------------------------------------------- */ |
565 | 1 | poDS->papszAuxLines = CSLLoad2(osAuxFilename, 1024, 1024, nullptr); |
566 | 1 | poDS->pszAuxFilename = CPLStrdup(osAuxFilename); |
567 | | |
568 | | /* -------------------------------------------------------------------- */ |
569 | | /* Find the RawDefinition line to establish overall parameters. */ |
570 | | /* -------------------------------------------------------------------- */ |
571 | 1 | pszLine = CSLFetchNameValue(poDS->papszAuxLines, "RawDefinition"); |
572 | | |
573 | | // It seems PCI now writes out .aux files without RawDefinition in |
574 | | // some cases. See bug 947. |
575 | 1 | if (pszLine == nullptr) |
576 | 1 | { |
577 | 1 | return nullptr; |
578 | 1 | } |
579 | | |
580 | 0 | const CPLStringList aosTokens(CSLTokenizeString(pszLine)); |
581 | |
|
582 | 0 | if (aosTokens.size() < 3) |
583 | 0 | { |
584 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
585 | 0 | "RawDefinition missing or corrupt in %s.", |
586 | 0 | poOpenInfo->pszFilename); |
587 | 0 | return nullptr; |
588 | 0 | } |
589 | | |
590 | 0 | poDS->nRasterXSize = atoi(aosTokens[0]); |
591 | 0 | poDS->nRasterYSize = atoi(aosTokens[1]); |
592 | 0 | int l_nBands = atoi(aosTokens[2]); |
593 | 0 | poDS->eAccess = poOpenInfo->eAccess; |
594 | |
|
595 | 0 | if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || |
596 | 0 | !GDALCheckBandCount(l_nBands, FALSE)) |
597 | 0 | { |
598 | 0 | return nullptr; |
599 | 0 | } |
600 | | |
601 | | /* -------------------------------------------------------------------- */ |
602 | | /* Open the file. */ |
603 | | /* -------------------------------------------------------------------- */ |
604 | 0 | if (poOpenInfo->eAccess == GA_Update) |
605 | 0 | { |
606 | 0 | poDS->fpImage = VSIFOpenL(osTarget, "rb+"); |
607 | |
|
608 | 0 | if (poDS->fpImage == nullptr) |
609 | 0 | { |
610 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
611 | 0 | "File %s is missing or read-only, check permissions.", |
612 | 0 | osTarget.c_str()); |
613 | 0 | return nullptr; |
614 | 0 | } |
615 | 0 | } |
616 | 0 | else |
617 | 0 | { |
618 | 0 | poDS->fpImage = VSIFOpenL(osTarget, "rb"); |
619 | |
|
620 | 0 | if (poDS->fpImage == nullptr) |
621 | 0 | { |
622 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
623 | 0 | "File %s is missing or unreadable.", osTarget.c_str()); |
624 | 0 | return nullptr; |
625 | 0 | } |
626 | 0 | } |
627 | | |
628 | | /* -------------------------------------------------------------------- */ |
629 | | /* Collect raw definitions of each channel and create */ |
630 | | /* corresponding bands. */ |
631 | | /* -------------------------------------------------------------------- */ |
632 | 0 | for (int i = 0; i < l_nBands; i++) |
633 | 0 | { |
634 | 0 | char szDefnName[32] = {'\0'}; |
635 | 0 | snprintf(szDefnName, sizeof(szDefnName), "ChanDefinition-%d", i + 1); |
636 | |
|
637 | 0 | pszLine = CSLFetchNameValue(poDS->papszAuxLines, szDefnName); |
638 | 0 | if (pszLine == nullptr) |
639 | 0 | { |
640 | 0 | continue; |
641 | 0 | } |
642 | | |
643 | 0 | const CPLStringList aosTokensBand(CSLTokenizeString(pszLine)); |
644 | 0 | if (aosTokensBand.size() < 4) |
645 | 0 | { |
646 | | // Skip the band with broken description |
647 | 0 | continue; |
648 | 0 | } |
649 | | |
650 | 0 | GDALDataType eType = GDT_Unknown; |
651 | 0 | if (EQUAL(aosTokensBand[0], "16U")) |
652 | 0 | eType = GDT_UInt16; |
653 | 0 | else if (EQUAL(aosTokensBand[0], "16S")) |
654 | 0 | eType = GDT_Int16; |
655 | 0 | else if (EQUAL(aosTokensBand[0], "32R")) |
656 | 0 | eType = GDT_Float32; |
657 | 0 | else |
658 | 0 | eType = GDT_Byte; |
659 | |
|
660 | 0 | bool bNative = true; |
661 | 0 | if (CSLCount(aosTokensBand) > 4) |
662 | 0 | { |
663 | 0 | #ifdef CPL_LSB |
664 | 0 | bNative = EQUAL(aosTokensBand[4], "Swapped"); |
665 | | #else |
666 | | bNative = EQUAL(aosTokensBand[4], "Unswapped"); |
667 | | #endif |
668 | 0 | } |
669 | |
|
670 | 0 | const vsi_l_offset nBandOffset = CPLScanUIntBig( |
671 | 0 | aosTokensBand[1], static_cast<int>(strlen(aosTokensBand[1]))); |
672 | 0 | const int nPixelOffset = atoi(aosTokensBand[2]); |
673 | 0 | const int nLineOffset = atoi(aosTokensBand[3]); |
674 | |
|
675 | 0 | if (nPixelOffset <= 0 || nLineOffset <= 0) |
676 | 0 | { |
677 | | // Skip the band with broken offsets. |
678 | 0 | continue; |
679 | 0 | } |
680 | | |
681 | 0 | auto poBand = std::make_unique<PAuxRasterBand>( |
682 | 0 | poDS.get(), poDS->nBands + 1, poDS->fpImage, nBandOffset, |
683 | 0 | nPixelOffset, nLineOffset, eType, bNative); |
684 | 0 | if (!poBand->IsValid()) |
685 | 0 | return nullptr; |
686 | 0 | poDS->SetBand(poDS->nBands + 1, std::move(poBand)); |
687 | 0 | } |
688 | | |
689 | | /* -------------------------------------------------------------------- */ |
690 | | /* Get the projection. */ |
691 | | /* -------------------------------------------------------------------- */ |
692 | 0 | const char *pszMapUnits = |
693 | 0 | CSLFetchNameValue(poDS->papszAuxLines, "MapUnits"); |
694 | 0 | const char *pszProjParams = |
695 | 0 | CSLFetchNameValue(poDS->papszAuxLines, "ProjParams"); |
696 | |
|
697 | 0 | if (pszMapUnits != nullptr) |
698 | 0 | { |
699 | 0 | poDS->m_oSRS = PCI2SRS(pszMapUnits, pszProjParams); |
700 | 0 | } |
701 | | |
702 | | /* -------------------------------------------------------------------- */ |
703 | | /* Initialize any PAM information. */ |
704 | | /* -------------------------------------------------------------------- */ |
705 | 0 | poDS->SetDescription(osTarget); |
706 | 0 | poDS->TryLoadXML(); |
707 | | |
708 | | /* -------------------------------------------------------------------- */ |
709 | | /* Check for overviews. */ |
710 | | /* -------------------------------------------------------------------- */ |
711 | 0 | poDS->oOvManager.Initialize(poDS.get(), osTarget); |
712 | |
|
713 | 0 | poDS->ScanForGCPs(); |
714 | |
|
715 | 0 | return poDS.release(); |
716 | 0 | } |
717 | | |
718 | | /************************************************************************/ |
719 | | /* GDALRegister_PAux() */ |
720 | | /************************************************************************/ |
721 | | |
722 | | void GDALRegister_PAux() |
723 | | |
724 | 22 | { |
725 | 22 | if (GDALGetDriverByName("PAux") != nullptr) |
726 | 0 | return; |
727 | | |
728 | 22 | GDALDriver *poDriver = new GDALDriver(); |
729 | | |
730 | 22 | poDriver->SetDescription("PAux"); |
731 | 22 | poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); |
732 | 22 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "PCI .aux Labelled"); |
733 | 22 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/paux.html"); |
734 | 22 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
735 | | |
736 | 22 | poDriver->pfnOpen = PAuxDataset::Open; |
737 | | |
738 | 22 | GetGDALDriverManager()->RegisterDriver(poDriver); |
739 | 22 | } |