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