Line data Source code
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 "ogr_spatialref.h"
17 : #include "rawdataset.h"
18 :
19 : #include <cmath>
20 :
21 : /************************************************************************/
22 : /* ==================================================================== */
23 : /* PAuxDataset */
24 : /* ==================================================================== */
25 : /************************************************************************/
26 :
27 : class PAuxRasterBand;
28 :
29 : class PAuxDataset final : public RawDataset
30 : {
31 : friend class PAuxRasterBand;
32 :
33 : VSILFILE *fpImage; // Image data file.
34 :
35 : int nGCPCount;
36 : GDAL_GCP *pasGCPList;
37 : OGRSpatialReference m_oGCPSRS{};
38 :
39 : void ScanForGCPs();
40 : static OGRSpatialReference PCI2SRS(const char *pszGeosys,
41 : const char *pszProjParams);
42 :
43 : OGRSpatialReference m_oSRS{};
44 :
45 : CPL_DISALLOW_COPY_ASSIGN(PAuxDataset)
46 :
47 : CPLErr Close() override;
48 :
49 : public:
50 : PAuxDataset();
51 : ~PAuxDataset() override;
52 :
53 : // TODO(schwehr): Why are these public?
54 : char *pszAuxFilename;
55 : char **papszAuxLines;
56 : int bAuxUpdated;
57 :
58 0 : const OGRSpatialReference *GetSpatialRef() const override
59 : {
60 0 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
61 : }
62 :
63 : CPLErr GetGeoTransform(double *) override;
64 : CPLErr SetGeoTransform(double *) override;
65 :
66 : int GetGCPCount() override;
67 :
68 0 : const OGRSpatialReference *GetGCPSpatialRef() const override
69 : {
70 0 : return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
71 : }
72 :
73 : const GDAL_GCP *GetGCPs() override;
74 :
75 : char **GetFileList() override;
76 :
77 : static GDALDataset *Open(GDALOpenInfo *);
78 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
79 : int nBandsIn, GDALDataType eType,
80 : char **papszParamList);
81 : };
82 :
83 : /************************************************************************/
84 : /* ==================================================================== */
85 : /* PAuxRasterBand */
86 : /* ==================================================================== */
87 : /************************************************************************/
88 :
89 : class PAuxRasterBand final : public RawRasterBand
90 : {
91 : CPL_DISALLOW_COPY_ASSIGN(PAuxRasterBand)
92 :
93 : public:
94 : PAuxRasterBand(GDALDataset *poDS, int nBand, VSILFILE *fpRaw,
95 : vsi_l_offset nImgOffset, int nPixelOffset, int nLineOffset,
96 : GDALDataType eDataType, int bNativeOrder);
97 :
98 : ~PAuxRasterBand() override;
99 :
100 : double GetNoDataValue(int *pbSuccess = nullptr) override;
101 : CPLErr SetNoDataValue(double) override;
102 :
103 : GDALColorTable *GetColorTable() override;
104 : GDALColorInterp GetColorInterpretation() override;
105 :
106 : void SetDescription(const char *pszNewDescription) override;
107 : };
108 :
109 : /************************************************************************/
110 : /* PAuxRasterBand() */
111 : /************************************************************************/
112 :
113 84 : PAuxRasterBand::PAuxRasterBand(GDALDataset *poDSIn, int nBandIn,
114 : VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
115 : int nPixelOffsetIn, int nLineOffsetIn,
116 84 : GDALDataType eDataTypeIn, int bNativeOrderIn)
117 : : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
118 : nLineOffsetIn, eDataTypeIn, bNativeOrderIn,
119 84 : RawRasterBand::OwnFP::NO)
120 : {
121 84 : PAuxDataset *poPDS = reinterpret_cast<PAuxDataset *>(poDS);
122 :
123 : /* -------------------------------------------------------------------- */
124 : /* Does this channel have a description? */
125 : /* -------------------------------------------------------------------- */
126 84 : char szTarget[128] = {'\0'};
127 :
128 84 : snprintf(szTarget, sizeof(szTarget), "ChanDesc-%d", nBand);
129 84 : if (CSLFetchNameValue(poPDS->papszAuxLines, szTarget) != nullptr)
130 0 : GDALRasterBand::SetDescription(
131 0 : CSLFetchNameValue(poPDS->papszAuxLines, szTarget));
132 :
133 : /* -------------------------------------------------------------------- */
134 : /* See if we have colors. Currently we must have color zero, */
135 : /* but this should not really be a limitation. */
136 : /* -------------------------------------------------------------------- */
137 84 : snprintf(szTarget, sizeof(szTarget), "METADATA_IMG_%d_Class_%d_Color",
138 : nBand, 0);
139 84 : if (CSLFetchNameValue(poPDS->papszAuxLines, szTarget) != nullptr)
140 : {
141 0 : poCT = new GDALColorTable();
142 :
143 0 : for (int i = 0; i < 256; i++)
144 : {
145 0 : snprintf(szTarget, sizeof(szTarget),
146 : "METADATA_IMG_%d_Class_%d_Color", nBand, i);
147 : const char *pszLine =
148 0 : CSLFetchNameValue(poPDS->papszAuxLines, szTarget);
149 0 : while (pszLine && *pszLine == ' ')
150 0 : pszLine++;
151 :
152 0 : int nRed = 0;
153 0 : int nGreen = 0;
154 0 : int nBlue = 0;
155 : // TODO(schwehr): Replace sscanf with something safe.
156 0 : if (pszLine != nullptr && STARTS_WITH_CI(pszLine, "(RGB:") &&
157 0 : sscanf(pszLine + 5, "%d %d %d", &nRed, &nGreen, &nBlue) == 3)
158 : {
159 0 : GDALColorEntry oColor = {static_cast<short>(nRed),
160 : static_cast<short>(nGreen),
161 0 : static_cast<short>(nBlue), 255};
162 :
163 0 : poCT->SetColorEntry(i, &oColor);
164 : }
165 : }
166 : }
167 84 : }
168 :
169 : /************************************************************************/
170 : /* ~PAuxRasterBand() */
171 : /************************************************************************/
172 :
173 168 : PAuxRasterBand::~PAuxRasterBand()
174 :
175 : {
176 168 : }
177 :
178 : /************************************************************************/
179 : /* GetNoDataValue() */
180 : /************************************************************************/
181 :
182 8 : double PAuxRasterBand::GetNoDataValue(int *pbSuccess)
183 :
184 : {
185 8 : char szTarget[128] = {'\0'};
186 8 : snprintf(szTarget, sizeof(szTarget), "METADATA_IMG_%d_NO_DATA_VALUE",
187 : nBand);
188 :
189 8 : PAuxDataset *poPDS = reinterpret_cast<PAuxDataset *>(poDS);
190 8 : const char *pszLine = CSLFetchNameValue(poPDS->papszAuxLines, szTarget);
191 :
192 8 : if (pbSuccess != nullptr)
193 8 : *pbSuccess = (pszLine != nullptr);
194 :
195 8 : if (pszLine == nullptr)
196 8 : return -1.0e8;
197 :
198 0 : return CPLAtof(pszLine);
199 : }
200 :
201 : /************************************************************************/
202 : /* SetNoDataValue() */
203 : /************************************************************************/
204 :
205 0 : CPLErr PAuxRasterBand::SetNoDataValue(double dfNewValue)
206 :
207 : {
208 0 : if (GetAccess() == GA_ReadOnly)
209 : {
210 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
211 : "Can't update readonly dataset.");
212 0 : return CE_Failure;
213 : }
214 :
215 0 : char szTarget[128] = {'\0'};
216 0 : char szValue[128] = {'\0'};
217 0 : snprintf(szTarget, sizeof(szTarget), "METADATA_IMG_%d_NO_DATA_VALUE",
218 : nBand);
219 0 : CPLsnprintf(szValue, sizeof(szValue), "%24.12f", dfNewValue);
220 :
221 0 : PAuxDataset *poPDS = reinterpret_cast<PAuxDataset *>(poDS);
222 0 : poPDS->papszAuxLines =
223 0 : CSLSetNameValue(poPDS->papszAuxLines, szTarget, szValue);
224 :
225 0 : poPDS->bAuxUpdated = TRUE;
226 :
227 0 : return CE_None;
228 : }
229 :
230 : /************************************************************************/
231 : /* SetDescription() */
232 : /* */
233 : /* We override the set description so we can mark the auxfile */
234 : /* info as changed. */
235 : /************************************************************************/
236 :
237 0 : void PAuxRasterBand::SetDescription(const char *pszNewDescription)
238 :
239 : {
240 0 : if (GetAccess() == GA_Update)
241 : {
242 0 : char szTarget[128] = {'\0'};
243 0 : snprintf(szTarget, sizeof(szTarget), "ChanDesc-%d", nBand);
244 :
245 0 : PAuxDataset *poPDS = reinterpret_cast<PAuxDataset *>(poDS);
246 0 : poPDS->papszAuxLines =
247 0 : CSLSetNameValue(poPDS->papszAuxLines, szTarget, pszNewDescription);
248 :
249 0 : poPDS->bAuxUpdated = TRUE;
250 : }
251 :
252 0 : GDALRasterBand::SetDescription(pszNewDescription);
253 0 : }
254 :
255 : /************************************************************************/
256 : /* GetColorTable() */
257 : /************************************************************************/
258 :
259 0 : GDALColorTable *PAuxRasterBand::GetColorTable()
260 :
261 : {
262 0 : return poCT;
263 : }
264 :
265 : /************************************************************************/
266 : /* GetColorInterpretation() */
267 : /************************************************************************/
268 :
269 2 : GDALColorInterp PAuxRasterBand::GetColorInterpretation()
270 :
271 : {
272 2 : if (poCT == nullptr)
273 2 : return GCI_Undefined;
274 :
275 0 : return GCI_PaletteIndex;
276 : }
277 :
278 : /************************************************************************/
279 : /* ==================================================================== */
280 : /* PAuxDataset */
281 : /* ==================================================================== */
282 : /************************************************************************/
283 :
284 : /************************************************************************/
285 : /* PAuxDataset() */
286 : /************************************************************************/
287 :
288 46 : PAuxDataset::PAuxDataset()
289 : : fpImage(nullptr), nGCPCount(0), pasGCPList(nullptr),
290 46 : pszAuxFilename(nullptr), papszAuxLines(nullptr), bAuxUpdated(FALSE)
291 : {
292 46 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
293 46 : m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
294 46 : }
295 :
296 : /************************************************************************/
297 : /* ~PAuxDataset() */
298 : /************************************************************************/
299 :
300 92 : PAuxDataset::~PAuxDataset()
301 :
302 : {
303 46 : PAuxDataset::Close();
304 92 : }
305 :
306 : /************************************************************************/
307 : /* Close() */
308 : /************************************************************************/
309 :
310 84 : CPLErr PAuxDataset::Close()
311 : {
312 84 : CPLErr eErr = CE_None;
313 84 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
314 : {
315 46 : if (PAuxDataset::FlushCache(true) != CE_None)
316 2 : eErr = CE_Failure;
317 :
318 46 : if (fpImage != nullptr)
319 : {
320 42 : if (VSIFCloseL(fpImage) != 0)
321 : {
322 0 : CPLError(CE_Failure, CPLE_FileIO, "I/O error");
323 0 : eErr = CE_Failure;
324 : }
325 : }
326 :
327 46 : if (bAuxUpdated)
328 : {
329 21 : CSLSetNameValueSeparator(papszAuxLines, ": ");
330 21 : CSLSave(papszAuxLines, pszAuxFilename);
331 : }
332 :
333 46 : GDALDeinitGCPs(nGCPCount, pasGCPList);
334 46 : CPLFree(pasGCPList);
335 :
336 46 : CPLFree(pszAuxFilename);
337 46 : CSLDestroy(papszAuxLines);
338 :
339 46 : if (GDALPamDataset::Close() != CE_None)
340 0 : eErr = CE_Failure;
341 : }
342 84 : return eErr;
343 : }
344 :
345 : /************************************************************************/
346 : /* GetFileList() */
347 : /************************************************************************/
348 :
349 1 : char **PAuxDataset::GetFileList()
350 :
351 : {
352 1 : char **papszFileList = RawDataset::GetFileList();
353 1 : papszFileList = CSLAddString(papszFileList, pszAuxFilename);
354 1 : return papszFileList;
355 : }
356 :
357 : /************************************************************************/
358 : /* PCI2SRS() */
359 : /* */
360 : /* Convert PCI coordinate system to WKT. For now this is very */
361 : /* incomplete, but can be filled out in the future. */
362 : /************************************************************************/
363 :
364 8 : OGRSpatialReference PAuxDataset::PCI2SRS(const char *pszGeosys,
365 : const char *pszProjParams)
366 :
367 : {
368 8 : while (*pszGeosys == ' ')
369 4 : pszGeosys++;
370 :
371 : /* -------------------------------------------------------------------- */
372 : /* Parse projection parameters array. */
373 : /* -------------------------------------------------------------------- */
374 4 : double adfProjParams[16] = {0.0};
375 :
376 4 : if (pszProjParams != nullptr)
377 : {
378 4 : char **papszTokens = CSLTokenizeString(pszProjParams);
379 :
380 4 : for (int i = 0;
381 68 : i < 16 && papszTokens != nullptr && papszTokens[i] != nullptr; i++)
382 64 : adfProjParams[i] = CPLAtof(papszTokens[i]);
383 :
384 4 : CSLDestroy(papszTokens);
385 : }
386 :
387 : /* -------------------------------------------------------------------- */
388 : /* Convert to SRS. */
389 : /* -------------------------------------------------------------------- */
390 4 : OGRSpatialReference oSRS;
391 4 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
392 4 : if (oSRS.importFromPCI(pszGeosys, nullptr, adfProjParams) != OGRERR_NONE)
393 : {
394 0 : oSRS.Clear();
395 : }
396 :
397 8 : return oSRS;
398 : }
399 :
400 : /************************************************************************/
401 : /* ScanForGCPs() */
402 : /************************************************************************/
403 :
404 42 : void PAuxDataset::ScanForGCPs()
405 :
406 : {
407 42 : const int MAX_GCP = 256;
408 :
409 42 : nGCPCount = 0;
410 42 : CPLAssert(pasGCPList == nullptr);
411 42 : pasGCPList =
412 42 : reinterpret_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), MAX_GCP));
413 :
414 : /* -------------------------------------------------------------------- */
415 : /* Get the GCP coordinate system. */
416 : /* -------------------------------------------------------------------- */
417 : const char *pszMapUnits =
418 42 : CSLFetchNameValue(papszAuxLines, "GCP_1_MapUnits");
419 : const char *pszProjParams =
420 42 : CSLFetchNameValue(papszAuxLines, "GCP_1_ProjParms");
421 :
422 42 : if (pszMapUnits != nullptr)
423 0 : m_oGCPSRS = PCI2SRS(pszMapUnits, pszProjParams);
424 :
425 : /* -------------------------------------------------------------------- */
426 : /* Collect standalone GCPs. They look like: */
427 : /* */
428 : /* GCP_1_n = row, col, x, y [,z [,"id"[, "desc"]]] */
429 : /* -------------------------------------------------------------------- */
430 42 : for (int i = 0; nGCPCount < MAX_GCP; i++)
431 : {
432 42 : char szName[50] = {'\0'};
433 42 : snprintf(szName, sizeof(szName), "GCP_1_%d", i + 1);
434 42 : if (CSLFetchNameValue(papszAuxLines, szName) == nullptr)
435 42 : break;
436 :
437 0 : char **papszTokens = CSLTokenizeStringComplex(
438 0 : CSLFetchNameValue(papszAuxLines, szName), " ", TRUE, FALSE);
439 :
440 0 : if (CSLCount(papszTokens) >= 4)
441 : {
442 0 : GDALInitGCPs(1, pasGCPList + nGCPCount);
443 :
444 0 : pasGCPList[nGCPCount].dfGCPX = CPLAtof(papszTokens[2]);
445 0 : pasGCPList[nGCPCount].dfGCPY = CPLAtof(papszTokens[3]);
446 0 : pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(papszTokens[0]);
447 0 : pasGCPList[nGCPCount].dfGCPLine = CPLAtof(papszTokens[1]);
448 :
449 0 : if (CSLCount(papszTokens) > 4)
450 0 : pasGCPList[nGCPCount].dfGCPZ = CPLAtof(papszTokens[4]);
451 :
452 0 : CPLFree(pasGCPList[nGCPCount].pszId);
453 0 : if (CSLCount(papszTokens) > 5)
454 : {
455 0 : pasGCPList[nGCPCount].pszId = CPLStrdup(papszTokens[5]);
456 : }
457 : else
458 : {
459 0 : snprintf(szName, sizeof(szName), "GCP_%d", i + 1);
460 0 : pasGCPList[nGCPCount].pszId = CPLStrdup(szName);
461 : }
462 :
463 0 : if (CSLCount(papszTokens) > 6)
464 : {
465 0 : CPLFree(pasGCPList[nGCPCount].pszInfo);
466 0 : pasGCPList[nGCPCount].pszInfo = CPLStrdup(papszTokens[6]);
467 : }
468 :
469 0 : nGCPCount++;
470 : }
471 :
472 0 : CSLDestroy(papszTokens);
473 : }
474 42 : }
475 :
476 : /************************************************************************/
477 : /* GetGCPCount() */
478 : /************************************************************************/
479 :
480 0 : int PAuxDataset::GetGCPCount()
481 :
482 : {
483 0 : return nGCPCount;
484 : }
485 :
486 : /************************************************************************/
487 : /* GetGCP() */
488 : /************************************************************************/
489 :
490 0 : const GDAL_GCP *PAuxDataset::GetGCPs()
491 :
492 : {
493 0 : return pasGCPList;
494 : }
495 :
496 : /************************************************************************/
497 : /* GetGeoTransform() */
498 : /************************************************************************/
499 :
500 12 : CPLErr PAuxDataset::GetGeoTransform(double *padfGeoTransform)
501 :
502 : {
503 12 : if (CSLFetchNameValue(papszAuxLines, "UpLeftX") == nullptr ||
504 12 : CSLFetchNameValue(papszAuxLines, "UpLeftY") == nullptr ||
505 36 : CSLFetchNameValue(papszAuxLines, "LoRightX") == nullptr ||
506 12 : CSLFetchNameValue(papszAuxLines, "LoRightY") == nullptr)
507 : {
508 0 : padfGeoTransform[0] = 0.0;
509 0 : padfGeoTransform[1] = 1.0;
510 0 : padfGeoTransform[2] = 0.0;
511 0 : padfGeoTransform[3] = 0.0;
512 0 : padfGeoTransform[4] = 0.0;
513 0 : padfGeoTransform[5] = 1.0;
514 :
515 0 : return CE_Failure;
516 : }
517 :
518 : const double dfUpLeftX =
519 12 : CPLAtof(CSLFetchNameValue(papszAuxLines, "UpLeftX"));
520 : const double dfUpLeftY =
521 12 : CPLAtof(CSLFetchNameValue(papszAuxLines, "UpLeftY"));
522 : const double dfLoRightX =
523 12 : CPLAtof(CSLFetchNameValue(papszAuxLines, "LoRightX"));
524 : const double dfLoRightY =
525 12 : CPLAtof(CSLFetchNameValue(papszAuxLines, "LoRightY"));
526 :
527 12 : padfGeoTransform[0] = dfUpLeftX;
528 12 : padfGeoTransform[1] = (dfLoRightX - dfUpLeftX) / GetRasterXSize();
529 12 : padfGeoTransform[2] = 0.0;
530 12 : padfGeoTransform[3] = dfUpLeftY;
531 12 : padfGeoTransform[4] = 0.0;
532 12 : padfGeoTransform[5] = (dfLoRightY - dfUpLeftY) / GetRasterYSize();
533 :
534 12 : return CE_None;
535 : }
536 :
537 : /************************************************************************/
538 : /* SetGeoTransform() */
539 : /************************************************************************/
540 :
541 21 : CPLErr PAuxDataset::SetGeoTransform(double *padfGeoTransform)
542 :
543 : {
544 21 : char szUpLeftX[128] = {'\0'};
545 21 : char szUpLeftY[128] = {'\0'};
546 21 : char szLoRightX[128] = {'\0'};
547 21 : char szLoRightY[128] = {'\0'};
548 :
549 40 : if (std::abs(padfGeoTransform[0]) < 181 &&
550 19 : std::abs(padfGeoTransform[1]) < 1)
551 : {
552 19 : CPLsnprintf(szUpLeftX, sizeof(szUpLeftX), "%.12f", padfGeoTransform[0]);
553 19 : CPLsnprintf(szUpLeftY, sizeof(szUpLeftY), "%.12f", padfGeoTransform[3]);
554 19 : CPLsnprintf(szLoRightX, sizeof(szLoRightX), "%.12f",
555 19 : padfGeoTransform[0] +
556 19 : padfGeoTransform[1] * GetRasterXSize());
557 19 : CPLsnprintf(szLoRightY, sizeof(szLoRightY), "%.12f",
558 19 : padfGeoTransform[3] +
559 19 : padfGeoTransform[5] * GetRasterYSize());
560 : }
561 : else
562 : {
563 2 : CPLsnprintf(szUpLeftX, sizeof(szUpLeftX), "%.3f", padfGeoTransform[0]);
564 2 : CPLsnprintf(szUpLeftY, sizeof(szUpLeftY), "%.3f", padfGeoTransform[3]);
565 2 : CPLsnprintf(szLoRightX, sizeof(szLoRightX), "%.3f",
566 2 : padfGeoTransform[0] +
567 2 : padfGeoTransform[1] * GetRasterXSize());
568 2 : CPLsnprintf(szLoRightY, sizeof(szLoRightY), "%.3f",
569 2 : padfGeoTransform[3] +
570 2 : padfGeoTransform[5] * GetRasterYSize());
571 : }
572 :
573 21 : papszAuxLines = CSLSetNameValue(papszAuxLines, "UpLeftX", szUpLeftX);
574 21 : papszAuxLines = CSLSetNameValue(papszAuxLines, "UpLeftY", szUpLeftY);
575 21 : papszAuxLines = CSLSetNameValue(papszAuxLines, "LoRightX", szLoRightX);
576 21 : papszAuxLines = CSLSetNameValue(papszAuxLines, "LoRightY", szLoRightY);
577 :
578 21 : bAuxUpdated = TRUE;
579 :
580 21 : return CE_None;
581 : }
582 :
583 : /************************************************************************/
584 : /* Open() */
585 : /************************************************************************/
586 :
587 30781 : GDALDataset *PAuxDataset::Open(GDALOpenInfo *poOpenInfo)
588 :
589 : {
590 30781 : if (poOpenInfo->nHeaderBytes < 1)
591 27247 : return nullptr;
592 :
593 : /* -------------------------------------------------------------------- */
594 : /* If this is an .aux file, fetch out and form the name of the */
595 : /* file it references. */
596 : /* -------------------------------------------------------------------- */
597 :
598 7068 : CPLString osTarget = poOpenInfo->pszFilename;
599 :
600 3536 : if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "aux") &&
601 2 : STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
602 : "AuxilaryTarget: "))
603 : {
604 2 : const char *pszSrc =
605 2 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader + 16);
606 :
607 2 : char szAuxTarget[1024] = {'\0'};
608 2 : for (int i = 0; i < static_cast<int>(sizeof(szAuxTarget)) - 1 &&
609 24 : pszSrc[i] != 10 && pszSrc[i] != 13 && pszSrc[i] != '\0';
610 : i++)
611 : {
612 22 : szAuxTarget[i] = pszSrc[i];
613 : }
614 2 : szAuxTarget[sizeof(szAuxTarget) - 1] = '\0';
615 :
616 2 : const std::string osPath(CPLGetPath(poOpenInfo->pszFilename));
617 2 : osTarget = CPLFormFilename(osPath.c_str(), szAuxTarget, nullptr);
618 : }
619 :
620 : /* -------------------------------------------------------------------- */
621 : /* Now we need to tear apart the filename to form a .aux */
622 : /* filename. */
623 : /* -------------------------------------------------------------------- */
624 7068 : CPLString osAuxFilename = CPLResetExtension(osTarget, "aux");
625 :
626 : /* -------------------------------------------------------------------- */
627 : /* Do we have a .aux file? */
628 : /* -------------------------------------------------------------------- */
629 3534 : char **papszSiblingFiles = poOpenInfo->GetSiblingFiles();
630 7032 : if (papszSiblingFiles != nullptr &&
631 3498 : CSLFindString(papszSiblingFiles, CPLGetFilename(osAuxFilename)) == -1)
632 : {
633 3449 : return nullptr;
634 : }
635 :
636 85 : VSILFILE *fp = VSIFOpenL(osAuxFilename, "r");
637 85 : if (fp == nullptr)
638 : {
639 36 : osAuxFilename = CPLResetExtension(osTarget, "AUX");
640 36 : fp = VSIFOpenL(osAuxFilename, "r");
641 : }
642 :
643 85 : if (fp == nullptr)
644 36 : return nullptr;
645 :
646 : /* -------------------------------------------------------------------- */
647 : /* Is this file a PCI .aux file? Check the first line for the */
648 : /* telltale AuxilaryTarget keyword. */
649 : /* */
650 : /* At this point we should be verifying that it refers to our */
651 : /* binary file, but that is a pretty involved test. */
652 : /* -------------------------------------------------------------------- */
653 49 : CPLPushErrorHandler(CPLQuietErrorHandler);
654 49 : const char *pszLine = CPLReadLine2L(fp, 1024, nullptr);
655 49 : CPLPopErrorHandler();
656 :
657 49 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
658 :
659 49 : if (pszLine == nullptr || (!STARTS_WITH_CI(pszLine, "AuxilaryTarget") &&
660 1 : !STARTS_WITH_CI(pszLine, "AuxiliaryTarget")))
661 : {
662 3 : CPLErrorReset();
663 3 : return nullptr;
664 : }
665 :
666 : /* -------------------------------------------------------------------- */
667 : /* Create a corresponding GDALDataset. */
668 : /* -------------------------------------------------------------------- */
669 92 : auto poDS = std::make_unique<PAuxDataset>();
670 :
671 : /* -------------------------------------------------------------------- */
672 : /* Load the .aux file into a string list suitable to be */
673 : /* searched with CSLFetchNameValue(). */
674 : /* -------------------------------------------------------------------- */
675 46 : poDS->papszAuxLines = CSLLoad2(osAuxFilename, 1024, 1024, nullptr);
676 46 : poDS->pszAuxFilename = CPLStrdup(osAuxFilename);
677 :
678 : /* -------------------------------------------------------------------- */
679 : /* Find the RawDefinition line to establish overall parameters. */
680 : /* -------------------------------------------------------------------- */
681 46 : pszLine = CSLFetchNameValue(poDS->papszAuxLines, "RawDefinition");
682 :
683 : // It seems PCI now writes out .aux files without RawDefinition in
684 : // some cases. See bug 947.
685 46 : if (pszLine == nullptr)
686 : {
687 2 : return nullptr;
688 : }
689 :
690 88 : const CPLStringList aosTokens(CSLTokenizeString(pszLine));
691 :
692 44 : if (aosTokens.size() < 3)
693 : {
694 0 : CPLError(CE_Failure, CPLE_AppDefined,
695 : "RawDefinition missing or corrupt in %s.",
696 : poOpenInfo->pszFilename);
697 0 : return nullptr;
698 : }
699 :
700 44 : poDS->nRasterXSize = atoi(aosTokens[0]);
701 44 : poDS->nRasterYSize = atoi(aosTokens[1]);
702 44 : int l_nBands = atoi(aosTokens[2]);
703 44 : poDS->eAccess = poOpenInfo->eAccess;
704 :
705 88 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
706 44 : !GDALCheckBandCount(l_nBands, FALSE))
707 : {
708 2 : return nullptr;
709 : }
710 :
711 : /* -------------------------------------------------------------------- */
712 : /* Open the file. */
713 : /* -------------------------------------------------------------------- */
714 42 : if (poOpenInfo->eAccess == GA_Update)
715 : {
716 25 : poDS->fpImage = VSIFOpenL(osTarget, "rb+");
717 :
718 25 : if (poDS->fpImage == nullptr)
719 : {
720 0 : CPLError(CE_Failure, CPLE_OpenFailed,
721 : "File %s is missing or read-only, check permissions.",
722 : osTarget.c_str());
723 0 : return nullptr;
724 : }
725 : }
726 : else
727 : {
728 17 : poDS->fpImage = VSIFOpenL(osTarget, "rb");
729 :
730 17 : if (poDS->fpImage == nullptr)
731 : {
732 0 : CPLError(CE_Failure, CPLE_OpenFailed,
733 : "File %s is missing or unreadable.", osTarget.c_str());
734 0 : return nullptr;
735 : }
736 : }
737 :
738 : /* -------------------------------------------------------------------- */
739 : /* Collect raw definitions of each channel and create */
740 : /* corresponding bands. */
741 : /* -------------------------------------------------------------------- */
742 130 : for (int i = 0; i < l_nBands; i++)
743 : {
744 88 : char szDefnName[32] = {'\0'};
745 88 : snprintf(szDefnName, sizeof(szDefnName), "ChanDefinition-%d", i + 1);
746 :
747 88 : pszLine = CSLFetchNameValue(poDS->papszAuxLines, szDefnName);
748 88 : if (pszLine == nullptr)
749 : {
750 4 : continue;
751 : }
752 :
753 84 : const CPLStringList aosTokensBand(CSLTokenizeString(pszLine));
754 84 : if (aosTokensBand.size() < 4)
755 : {
756 : // Skip the band with broken description
757 0 : continue;
758 : }
759 :
760 84 : GDALDataType eType = GDT_Unknown;
761 84 : if (EQUAL(aosTokensBand[0], "16U"))
762 17 : eType = GDT_UInt16;
763 67 : else if (EQUAL(aosTokensBand[0], "16S"))
764 9 : eType = GDT_Int16;
765 58 : else if (EQUAL(aosTokensBand[0], "32R"))
766 9 : eType = GDT_Float32;
767 : else
768 49 : eType = GDT_Byte;
769 :
770 84 : bool bNative = true;
771 84 : if (CSLCount(aosTokensBand) > 4)
772 : {
773 : #ifdef CPL_LSB
774 84 : bNative = EQUAL(aosTokensBand[4], "Swapped");
775 : #else
776 : bNative = EQUAL(aosTokensBand[4], "Unswapped");
777 : #endif
778 : }
779 :
780 84 : const vsi_l_offset nBandOffset = CPLScanUIntBig(
781 84 : aosTokensBand[1], static_cast<int>(strlen(aosTokensBand[1])));
782 84 : const int nPixelOffset = atoi(aosTokensBand[2]);
783 84 : const int nLineOffset = atoi(aosTokensBand[3]);
784 :
785 84 : if (nPixelOffset <= 0 || nLineOffset <= 0)
786 : {
787 : // Skip the band with broken offsets.
788 0 : continue;
789 : }
790 :
791 : auto poBand = std::make_unique<PAuxRasterBand>(
792 84 : poDS.get(), poDS->nBands + 1, poDS->fpImage, nBandOffset,
793 84 : nPixelOffset, nLineOffset, eType, bNative);
794 84 : if (!poBand->IsValid())
795 0 : return nullptr;
796 84 : poDS->SetBand(poDS->nBands + 1, std::move(poBand));
797 : }
798 :
799 : /* -------------------------------------------------------------------- */
800 : /* Get the projection. */
801 : /* -------------------------------------------------------------------- */
802 : const char *pszMapUnits =
803 42 : CSLFetchNameValue(poDS->papszAuxLines, "MapUnits");
804 : const char *pszProjParams =
805 42 : CSLFetchNameValue(poDS->papszAuxLines, "ProjParams");
806 :
807 42 : if (pszMapUnits != nullptr)
808 : {
809 4 : poDS->m_oSRS = PCI2SRS(pszMapUnits, pszProjParams);
810 : }
811 :
812 : /* -------------------------------------------------------------------- */
813 : /* Initialize any PAM information. */
814 : /* -------------------------------------------------------------------- */
815 42 : poDS->SetDescription(osTarget);
816 42 : poDS->TryLoadXML();
817 :
818 : /* -------------------------------------------------------------------- */
819 : /* Check for overviews. */
820 : /* -------------------------------------------------------------------- */
821 42 : poDS->oOvManager.Initialize(poDS.get(), osTarget);
822 :
823 42 : poDS->ScanForGCPs();
824 42 : poDS->bAuxUpdated = FALSE;
825 :
826 42 : return poDS.release();
827 : }
828 :
829 : /************************************************************************/
830 : /* Create() */
831 : /************************************************************************/
832 :
833 62 : GDALDataset *PAuxDataset::Create(const char *pszFilename, int nXSize,
834 : int nYSize, int nBandsIn, GDALDataType eType,
835 : char **papszOptions)
836 :
837 : {
838 62 : const char *pszInterleave = CSLFetchNameValue(papszOptions, "INTERLEAVE");
839 62 : if (pszInterleave == nullptr)
840 62 : pszInterleave = "BAND";
841 :
842 : /* -------------------------------------------------------------------- */
843 : /* Verify input options. */
844 : /* -------------------------------------------------------------------- */
845 62 : if (eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 &&
846 : eType != GDT_Int16)
847 : {
848 27 : CPLError(CE_Failure, CPLE_AppDefined,
849 : "Attempt to create PCI .Aux labelled dataset with an illegal\n"
850 : "data type (%s).\n",
851 : GDALGetDataTypeName(eType));
852 :
853 27 : return nullptr;
854 : }
855 :
856 : /* -------------------------------------------------------------------- */
857 : /* Sum the sizes of the band pixel types. */
858 : /* -------------------------------------------------------------------- */
859 35 : int nPixelSizeSum = 0;
860 :
861 95 : for (int iBand = 0; iBand < nBandsIn; iBand++)
862 60 : nPixelSizeSum += GDALGetDataTypeSizeBytes(eType);
863 :
864 : /* -------------------------------------------------------------------- */
865 : /* Try to create the file. */
866 : /* -------------------------------------------------------------------- */
867 35 : VSILFILE *fp = VSIFOpenL(pszFilename, "w");
868 35 : if (fp == nullptr)
869 : {
870 3 : CPLError(CE_Failure, CPLE_OpenFailed,
871 : "Attempt to create file `%s' failed.\n", pszFilename);
872 3 : return nullptr;
873 : }
874 :
875 : /* -------------------------------------------------------------------- */
876 : /* Just write out a couple of bytes to establish the binary */
877 : /* file, and then close it. */
878 : /* -------------------------------------------------------------------- */
879 32 : CPL_IGNORE_RET_VAL(VSIFWriteL("\0\0", 2, 1, fp));
880 32 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
881 :
882 : /* -------------------------------------------------------------------- */
883 : /* Create the aux filename. */
884 : /* -------------------------------------------------------------------- */
885 : char *pszAuxFilename =
886 32 : static_cast<char *>(CPLMalloc(strlen(pszFilename) + 5));
887 32 : strcpy(pszAuxFilename, pszFilename);
888 :
889 975 : for (int i = static_cast<int>(strlen(pszAuxFilename)) - 1; i > 0; i--)
890 : {
891 945 : if (pszAuxFilename[i] == '.')
892 : {
893 2 : pszAuxFilename[i] = '\0';
894 2 : break;
895 : }
896 : }
897 :
898 32 : strcat(pszAuxFilename, ".aux");
899 :
900 : /* -------------------------------------------------------------------- */
901 : /* Open the file. */
902 : /* -------------------------------------------------------------------- */
903 32 : fp = VSIFOpenL(pszAuxFilename, "wt");
904 32 : if (fp == nullptr)
905 : {
906 0 : CPLError(CE_Failure, CPLE_OpenFailed,
907 : "Attempt to create file `%s' failed.\n", pszAuxFilename);
908 0 : return nullptr;
909 : }
910 32 : CPLFree(pszAuxFilename);
911 :
912 : /* -------------------------------------------------------------------- */
913 : /* We need to write out the original filename but without any */
914 : /* path components in the AuxilaryTarget line. Do so now. */
915 : /* -------------------------------------------------------------------- */
916 32 : int iStart = static_cast<int>(strlen(pszFilename)) - 1;
917 273 : while (iStart > 0 && pszFilename[iStart - 1] != '/' &&
918 241 : pszFilename[iStart - 1] != '\\')
919 241 : iStart--;
920 :
921 32 : CPL_IGNORE_RET_VAL(
922 32 : VSIFPrintfL(fp, "AuxilaryTarget: %s\n", pszFilename + iStart));
923 :
924 : /* -------------------------------------------------------------------- */
925 : /* Write out the raw definition for the dataset as a whole. */
926 : /* -------------------------------------------------------------------- */
927 32 : CPL_IGNORE_RET_VAL(
928 32 : VSIFPrintfL(fp, "RawDefinition: %d %d %d\n", nXSize, nYSize, nBandsIn));
929 :
930 : /* -------------------------------------------------------------------- */
931 : /* Write out a definition for each band. We always write band */
932 : /* sequential files for now as these are pretty efficiently */
933 : /* handled by GDAL. */
934 : /* -------------------------------------------------------------------- */
935 32 : vsi_l_offset nImgOffset = 0;
936 :
937 89 : for (int iBand = 0; iBand < nBandsIn; iBand++)
938 : {
939 57 : int nPixelOffset = 0;
940 57 : int nLineOffset = 0;
941 57 : vsi_l_offset nNextImgOffset = 0;
942 :
943 : /* --------------------------------------------------------------------
944 : */
945 : /* Establish our file layout based on supplied interleaving. */
946 : /* --------------------------------------------------------------------
947 : */
948 57 : if (EQUAL(pszInterleave, "LINE"))
949 : {
950 0 : nPixelOffset = GDALGetDataTypeSizeBytes(eType);
951 0 : nLineOffset = nXSize * nPixelSizeSum;
952 0 : nNextImgOffset =
953 0 : nImgOffset + static_cast<vsi_l_offset>(nPixelOffset) * nXSize;
954 : }
955 57 : else if (EQUAL(pszInterleave, "PIXEL"))
956 : {
957 0 : nPixelOffset = nPixelSizeSum;
958 0 : nLineOffset = nXSize * nPixelOffset;
959 0 : nNextImgOffset = nImgOffset + GDALGetDataTypeSizeBytes(eType);
960 : }
961 : else /* default to band */
962 : {
963 57 : nPixelOffset = GDALGetDataTypeSize(eType) / 8;
964 57 : nLineOffset = nXSize * nPixelOffset;
965 57 : nNextImgOffset =
966 57 : nImgOffset + nYSize * static_cast<vsi_l_offset>(nLineOffset);
967 : }
968 :
969 : /* --------------------------------------------------------------------
970 : */
971 : /* Write out line indicating layout. */
972 : /* --------------------------------------------------------------------
973 : */
974 57 : const char *pszTypeName = nullptr;
975 57 : if (eType == GDT_Float32)
976 5 : pszTypeName = "32R";
977 52 : else if (eType == GDT_Int16)
978 5 : pszTypeName = "16S";
979 47 : else if (eType == GDT_UInt16)
980 5 : pszTypeName = "16U";
981 : else
982 42 : pszTypeName = "8U";
983 :
984 57 : CPL_IGNORE_RET_VAL(VSIFPrintfL(
985 : fp, "ChanDefinition-%d: %s " CPL_FRMT_GIB " %d %d %s\n", iBand + 1,
986 : pszTypeName, static_cast<GIntBig>(nImgOffset), nPixelOffset,
987 : nLineOffset,
988 : #ifdef CPL_LSB
989 : "Swapped"
990 : #else
991 : "Unswapped"
992 : #endif
993 : ));
994 :
995 57 : nImgOffset = nNextImgOffset;
996 : }
997 :
998 : /* -------------------------------------------------------------------- */
999 : /* Cleanup */
1000 : /* -------------------------------------------------------------------- */
1001 32 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
1002 :
1003 32 : return static_cast<GDALDataset *>(GDALOpen(pszFilename, GA_Update));
1004 : }
1005 :
1006 : /************************************************************************/
1007 : /* PAuxDelete() */
1008 : /************************************************************************/
1009 :
1010 6 : static CPLErr PAuxDelete(const char *pszBasename)
1011 :
1012 : {
1013 6 : VSILFILE *fp = VSIFOpenL(CPLResetExtension(pszBasename, "aux"), "r");
1014 6 : if (fp == nullptr)
1015 : {
1016 0 : CPLError(CE_Failure, CPLE_AppDefined,
1017 : "%s does not appear to be a PAux dataset: "
1018 : "there is no .aux file.",
1019 : pszBasename);
1020 0 : return CE_Failure;
1021 : }
1022 :
1023 6 : const char *pszLine = CPLReadLineL(fp);
1024 6 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
1025 :
1026 6 : if (pszLine == nullptr || !STARTS_WITH_CI(pszLine, "AuxilaryTarget"))
1027 : {
1028 0 : CPLError(CE_Failure, CPLE_AppDefined,
1029 : "%s does not appear to be a PAux dataset:"
1030 : "the .aux file does not start with AuxilaryTarget",
1031 : pszBasename);
1032 0 : return CE_Failure;
1033 : }
1034 :
1035 6 : if (VSIUnlink(pszBasename) != 0)
1036 : {
1037 0 : CPLError(CE_Failure, CPLE_AppDefined, "OS unlinking file %s.",
1038 : pszBasename);
1039 0 : return CE_Failure;
1040 : }
1041 :
1042 6 : VSIUnlink(CPLResetExtension(pszBasename, "aux"));
1043 :
1044 6 : return CE_None;
1045 : }
1046 :
1047 : /************************************************************************/
1048 : /* GDALRegister_PAux() */
1049 : /************************************************************************/
1050 :
1051 1595 : void GDALRegister_PAux()
1052 :
1053 : {
1054 1595 : if (GDALGetDriverByName("PAux") != nullptr)
1055 302 : return;
1056 :
1057 1293 : GDALDriver *poDriver = new GDALDriver();
1058 :
1059 1293 : poDriver->SetDescription("PAux");
1060 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1061 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "PCI .aux Labelled");
1062 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/paux.html");
1063 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1064 1293 : "Byte Int16 UInt16 Float32");
1065 1293 : poDriver->SetMetadataItem(
1066 : GDAL_DMD_CREATIONOPTIONLIST,
1067 : "<CreationOptionList>"
1068 : " <Option name='INTERLEAVE' type='string-select' default='BAND'>"
1069 : " <Value>BAND</Value>"
1070 : " <Value>LINE</Value>"
1071 : " <Value>PIXEL</Value>"
1072 : " </Option>"
1073 1293 : "</CreationOptionList>");
1074 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1075 :
1076 1293 : poDriver->pfnOpen = PAuxDataset::Open;
1077 1293 : poDriver->pfnCreate = PAuxDataset::Create;
1078 1293 : poDriver->pfnDelete = PAuxDelete;
1079 :
1080 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
1081 : }
|