Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Implements the Golden Software Binary Grid Format.
5 : * Author: Kevin Locke, kwl7@cornell.edu
6 : * (Based largely on aaigriddataset.cpp by Frank Warmerdam)
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2006, Kevin Locke <kwl7@cornell.edu>
10 : * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_conv.h"
16 :
17 : #include <assert.h>
18 : #include <float.h>
19 : #include <limits.h>
20 : #include <limits>
21 :
22 : #include "gdal_frmts.h"
23 : #include "gdal_pam.h"
24 :
25 : /************************************************************************/
26 : /* ==================================================================== */
27 : /* GSBGDataset */
28 : /* ==================================================================== */
29 : /************************************************************************/
30 :
31 : class GSBGRasterBand;
32 :
33 : class GSBGDataset final : public GDALPamDataset
34 : {
35 : friend class GSBGRasterBand;
36 :
37 : static const float fNODATA_VALUE;
38 : static const size_t nHEADER_SIZE;
39 :
40 : static CPLErr WriteHeader(VSILFILE *fp, int nXSize, int nYSize,
41 : double dfMinX, double dfMaxX, double dfMinY,
42 : double dfMaxY, double dfMinZ, double dfMaxZ);
43 :
44 : VSILFILE *fp;
45 :
46 : public:
47 45 : GSBGDataset() : fp(nullptr)
48 : {
49 45 : }
50 :
51 : ~GSBGDataset();
52 :
53 : static int Identify(GDALOpenInfo *);
54 : static GDALDataset *Open(GDALOpenInfo *);
55 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
56 : int nBands, GDALDataType eType,
57 : char **papszParamList);
58 : static GDALDataset *CreateCopy(const char *pszFilename,
59 : GDALDataset *poSrcDS, int bStrict,
60 : char **papszOptions,
61 : GDALProgressFunc pfnProgress,
62 : void *pProgressData);
63 :
64 : CPLErr GetGeoTransform(double *padfGeoTransform) override;
65 : CPLErr SetGeoTransform(double *padfGeoTransform) override;
66 : };
67 :
68 : /* NOTE: This is not mentioned in the spec, but Surfer 8 uses this value */
69 : /* 0x7effffee (Little Endian: eeffff7e) */
70 : const float GSBGDataset::fNODATA_VALUE = 1.701410009187828e+38f;
71 :
72 : const size_t GSBGDataset::nHEADER_SIZE = 56;
73 :
74 : /************************************************************************/
75 : /* ==================================================================== */
76 : /* GSBGRasterBand */
77 : /* ==================================================================== */
78 : /************************************************************************/
79 :
80 : class GSBGRasterBand final : public GDALPamRasterBand
81 : {
82 : friend class GSBGDataset;
83 :
84 : double dfMinX;
85 : double dfMaxX;
86 : double dfMinY;
87 : double dfMaxY;
88 : double dfMinZ;
89 : double dfMaxZ;
90 :
91 : float *pafRowMinZ;
92 : float *pafRowMaxZ;
93 : int nMinZRow;
94 : int nMaxZRow;
95 :
96 : CPLErr ScanForMinMaxZ();
97 :
98 : public:
99 : GSBGRasterBand(GSBGDataset *, int);
100 : ~GSBGRasterBand();
101 :
102 : CPLErr IReadBlock(int, int, void *) override;
103 : CPLErr IWriteBlock(int, int, void *) override;
104 :
105 : double GetNoDataValue(int *pbSuccess = nullptr) override;
106 : double GetMinimum(int *pbSuccess = nullptr) override;
107 : double GetMaximum(int *pbSuccess = nullptr) override;
108 : };
109 :
110 : /************************************************************************/
111 : /* GSBGRasterBand() */
112 : /************************************************************************/
113 :
114 45 : GSBGRasterBand::GSBGRasterBand(GSBGDataset *poDSIn, int nBandIn)
115 : : dfMinX(0.0), dfMaxX(0.0), dfMinY(0.0), dfMaxY(0.0), dfMinZ(0.0),
116 : dfMaxZ(0.0), pafRowMinZ(nullptr), pafRowMaxZ(nullptr), nMinZRow(-1),
117 45 : nMaxZRow(-1)
118 : {
119 45 : this->poDS = poDSIn;
120 45 : this->nBand = nBandIn;
121 :
122 45 : eDataType = GDT_Float32;
123 :
124 45 : nBlockXSize = poDS->GetRasterXSize();
125 45 : nBlockYSize = 1;
126 45 : }
127 :
128 : /************************************************************************/
129 : /* ~GSBGRasterBand() */
130 : /************************************************************************/
131 :
132 90 : GSBGRasterBand::~GSBGRasterBand()
133 :
134 : {
135 45 : if (pafRowMinZ != nullptr)
136 1 : CPLFree(pafRowMinZ);
137 45 : if (pafRowMaxZ != nullptr)
138 1 : CPLFree(pafRowMaxZ);
139 90 : }
140 :
141 : /************************************************************************/
142 : /* ScanForMinMaxZ() */
143 : /************************************************************************/
144 :
145 1 : CPLErr GSBGRasterBand::ScanForMinMaxZ()
146 :
147 : {
148 1 : float *pafRowVals = (float *)VSI_MALLOC2_VERBOSE(nRasterXSize, 4);
149 :
150 1 : if (pafRowVals == nullptr)
151 : {
152 0 : return CE_Failure;
153 : }
154 :
155 1 : double dfNewMinZ = std::numeric_limits<double>::max();
156 1 : double dfNewMaxZ = std::numeric_limits<double>::lowest();
157 1 : int nNewMinZRow = 0;
158 1 : int nNewMaxZRow = 0;
159 :
160 : /* Since we have to scan, lets calc. statistics too */
161 1 : double dfSum = 0.0;
162 1 : double dfSum2 = 0.0;
163 1 : unsigned long nValuesRead = 0;
164 21 : for (int iRow = 0; iRow < nRasterYSize; iRow++)
165 : {
166 20 : CPLErr eErr = IReadBlock(0, iRow, pafRowVals);
167 20 : if (eErr != CE_None)
168 : {
169 0 : VSIFree(pafRowVals);
170 0 : return CE_Failure;
171 : }
172 :
173 20 : pafRowMinZ[iRow] = std::numeric_limits<float>::max();
174 20 : pafRowMaxZ[iRow] = std::numeric_limits<float>::lowest();
175 420 : for (int iCol = 0; iCol < nRasterXSize; iCol++)
176 : {
177 400 : if (pafRowVals[iCol] == GSBGDataset::fNODATA_VALUE)
178 400 : continue;
179 :
180 0 : if (pafRowVals[iCol] < pafRowMinZ[iRow])
181 0 : pafRowMinZ[iRow] = pafRowVals[iCol];
182 :
183 0 : if (pafRowVals[iCol] > pafRowMinZ[iRow])
184 0 : pafRowMaxZ[iRow] = pafRowVals[iCol];
185 :
186 0 : dfSum += pafRowVals[iCol];
187 0 : dfSum2 += static_cast<double>(pafRowVals[iCol]) * pafRowVals[iCol];
188 0 : nValuesRead++;
189 : }
190 :
191 20 : if (pafRowMinZ[iRow] < dfNewMinZ)
192 : {
193 1 : dfNewMinZ = pafRowMinZ[iRow];
194 1 : nNewMinZRow = iRow;
195 : }
196 :
197 20 : if (pafRowMaxZ[iRow] > dfNewMaxZ)
198 : {
199 1 : dfNewMaxZ = pafRowMaxZ[iRow];
200 1 : nNewMaxZRow = iRow;
201 : }
202 : }
203 :
204 1 : VSIFree(pafRowVals);
205 :
206 1 : if (nValuesRead == 0)
207 : {
208 1 : dfMinZ = 0.0;
209 1 : dfMaxZ = 0.0;
210 1 : nMinZRow = 0;
211 1 : nMaxZRow = 0;
212 1 : return CE_None;
213 : }
214 :
215 0 : dfMinZ = dfNewMinZ;
216 0 : dfMaxZ = dfNewMaxZ;
217 0 : nMinZRow = nNewMinZRow;
218 0 : nMaxZRow = nNewMaxZRow;
219 :
220 0 : double dfMean = dfSum / nValuesRead;
221 0 : double dfStdDev = sqrt((dfSum2 / nValuesRead) - (dfMean * dfMean));
222 0 : SetStatistics(dfMinZ, dfMaxZ, dfMean, dfStdDev);
223 :
224 0 : return CE_None;
225 : }
226 :
227 : /************************************************************************/
228 : /* IReadBlock() */
229 : /************************************************************************/
230 :
231 140 : CPLErr GSBGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
232 :
233 : {
234 140 : if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
235 0 : return CE_Failure;
236 :
237 140 : GSBGDataset *poGDS = reinterpret_cast<GSBGDataset *>(poDS);
238 280 : if (VSIFSeekL(poGDS->fp,
239 140 : GSBGDataset::nHEADER_SIZE +
240 140 : 4 * static_cast<vsi_l_offset>(nRasterXSize) *
241 140 : (nRasterYSize - nBlockYOff - 1),
242 140 : SEEK_SET) != 0)
243 : {
244 0 : CPLError(CE_Failure, CPLE_FileIO,
245 : "Unable to seek to beginning of grid row.\n");
246 0 : return CE_Failure;
247 : }
248 :
249 140 : if (VSIFReadL(pImage, sizeof(float), nBlockXSize, poGDS->fp) !=
250 140 : static_cast<unsigned>(nBlockXSize))
251 : {
252 0 : CPLError(CE_Failure, CPLE_FileIO,
253 : "Unable to read block from grid file.\n");
254 0 : return CE_Failure;
255 : }
256 :
257 : #ifdef CPL_MSB
258 : float *pfImage = (float *)pImage;
259 : for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
260 : {
261 : CPL_LSBPTR32(pfImage + iPixel);
262 : }
263 : #endif
264 :
265 140 : return CE_None;
266 : }
267 :
268 : /************************************************************************/
269 : /* IWriteBlock() */
270 : /************************************************************************/
271 :
272 20 : CPLErr GSBGRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
273 :
274 : {
275 20 : if (eAccess == GA_ReadOnly)
276 : {
277 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
278 : "Unable to write block, dataset opened read only.\n");
279 0 : return CE_Failure;
280 : }
281 :
282 20 : if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
283 0 : return CE_Failure;
284 :
285 20 : GSBGDataset *poGDS = cpl::down_cast<GSBGDataset *>(poDS);
286 :
287 20 : if (pafRowMinZ == nullptr || pafRowMaxZ == nullptr || nMinZRow < 0 ||
288 19 : nMaxZRow < 0)
289 : {
290 1 : pafRowMinZ = (float *)VSI_MALLOC2_VERBOSE(nRasterYSize, sizeof(float));
291 1 : if (pafRowMinZ == nullptr)
292 : {
293 0 : return CE_Failure;
294 : }
295 :
296 1 : pafRowMaxZ = (float *)VSI_MALLOC2_VERBOSE(nRasterYSize, sizeof(float));
297 1 : if (pafRowMaxZ == nullptr)
298 : {
299 0 : VSIFree(pafRowMinZ);
300 0 : pafRowMinZ = nullptr;
301 0 : return CE_Failure;
302 : }
303 :
304 1 : CPLErr eErr = ScanForMinMaxZ();
305 1 : if (eErr != CE_None)
306 0 : return eErr;
307 : }
308 :
309 40 : if (VSIFSeekL(poGDS->fp,
310 20 : GSBGDataset::nHEADER_SIZE +
311 20 : static_cast<vsi_l_offset>(4) * nRasterXSize *
312 20 : (nRasterYSize - nBlockYOff - 1),
313 20 : SEEK_SET) != 0)
314 : {
315 0 : CPLError(CE_Failure, CPLE_FileIO,
316 : "Unable to seek to beginning of grid row.\n");
317 0 : return CE_Failure;
318 : }
319 :
320 20 : float *pfImage = (float *)pImage;
321 20 : pafRowMinZ[nBlockYOff] = std::numeric_limits<float>::max();
322 20 : pafRowMaxZ[nBlockYOff] = std::numeric_limits<float>::lowest();
323 420 : for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
324 : {
325 400 : if (pfImage[iPixel] != GSBGDataset::fNODATA_VALUE)
326 : {
327 400 : if (pfImage[iPixel] < pafRowMinZ[nBlockYOff])
328 78 : pafRowMinZ[nBlockYOff] = pfImage[iPixel];
329 :
330 400 : if (pfImage[iPixel] > pafRowMaxZ[nBlockYOff])
331 43 : pafRowMaxZ[nBlockYOff] = pfImage[iPixel];
332 : }
333 :
334 400 : CPL_LSBPTR32(pfImage + iPixel);
335 : }
336 :
337 20 : if (VSIFWriteL(pImage, sizeof(float), nBlockXSize, poGDS->fp) !=
338 20 : static_cast<unsigned>(nBlockXSize))
339 : {
340 0 : CPLError(CE_Failure, CPLE_FileIO,
341 : "Unable to write block to grid file.\n");
342 0 : return CE_Failure;
343 : }
344 :
345 : /* Update min/max Z values as appropriate */
346 20 : bool bHeaderNeedsUpdate = false;
347 20 : if (nMinZRow == nBlockYOff && pafRowMinZ[nBlockYOff] > dfMinZ)
348 : {
349 1 : double dfNewMinZ = std::numeric_limits<double>::max();
350 21 : for (int iRow = 0; iRow < nRasterYSize; iRow++)
351 : {
352 20 : if (pafRowMinZ[iRow] < dfNewMinZ)
353 : {
354 1 : dfNewMinZ = pafRowMinZ[iRow];
355 1 : nMinZRow = iRow;
356 : }
357 : }
358 :
359 1 : if (dfNewMinZ != dfMinZ)
360 : {
361 1 : dfMinZ = dfNewMinZ;
362 1 : bHeaderNeedsUpdate = true;
363 : }
364 : }
365 :
366 20 : if (nMaxZRow == nBlockYOff && pafRowMaxZ[nBlockYOff] < dfMaxZ)
367 : {
368 0 : double dfNewMaxZ = std::numeric_limits<double>::lowest();
369 0 : for (int iRow = 0; iRow < nRasterYSize; iRow++)
370 : {
371 0 : if (pafRowMaxZ[iRow] > dfNewMaxZ)
372 : {
373 0 : dfNewMaxZ = pafRowMaxZ[iRow];
374 0 : nMaxZRow = iRow;
375 : }
376 : }
377 :
378 0 : if (dfNewMaxZ != dfMaxZ)
379 : {
380 0 : dfMaxZ = dfNewMaxZ;
381 0 : bHeaderNeedsUpdate = true;
382 : }
383 : }
384 :
385 20 : if (pafRowMinZ[nBlockYOff] < dfMinZ || pafRowMaxZ[nBlockYOff] > dfMaxZ)
386 : {
387 6 : if (pafRowMinZ[nBlockYOff] < dfMinZ)
388 : {
389 3 : dfMinZ = pafRowMinZ[nBlockYOff];
390 3 : nMinZRow = nBlockYOff;
391 : }
392 :
393 6 : if (pafRowMaxZ[nBlockYOff] > dfMaxZ)
394 : {
395 4 : dfMaxZ = pafRowMaxZ[nBlockYOff];
396 4 : nMaxZRow = nBlockYOff;
397 : }
398 :
399 6 : bHeaderNeedsUpdate = true;
400 : }
401 :
402 20 : if (bHeaderNeedsUpdate && dfMaxZ > dfMinZ)
403 : {
404 : CPLErr eErr =
405 6 : poGDS->WriteHeader(poGDS->fp, nRasterXSize, nRasterYSize, dfMinX,
406 : dfMaxX, dfMinY, dfMaxY, dfMinZ, dfMaxZ);
407 6 : return eErr;
408 : }
409 :
410 14 : return CE_None;
411 : }
412 :
413 : /************************************************************************/
414 : /* GetNoDataValue() */
415 : /************************************************************************/
416 :
417 21 : double GSBGRasterBand::GetNoDataValue(int *pbSuccess)
418 : {
419 21 : if (pbSuccess)
420 15 : *pbSuccess = TRUE;
421 :
422 21 : return GSBGDataset::fNODATA_VALUE;
423 : }
424 :
425 : /************************************************************************/
426 : /* GetMinimum() */
427 : /************************************************************************/
428 :
429 0 : double GSBGRasterBand::GetMinimum(int *pbSuccess)
430 : {
431 0 : if (pbSuccess)
432 0 : *pbSuccess = TRUE;
433 :
434 0 : return dfMinZ;
435 : }
436 :
437 : /************************************************************************/
438 : /* GetMaximum() */
439 : /************************************************************************/
440 :
441 0 : double GSBGRasterBand::GetMaximum(int *pbSuccess)
442 : {
443 0 : if (pbSuccess)
444 0 : *pbSuccess = TRUE;
445 :
446 0 : return dfMaxZ;
447 : }
448 :
449 : /************************************************************************/
450 : /* ==================================================================== */
451 : /* GSBGDataset */
452 : /* ==================================================================== */
453 : /************************************************************************/
454 :
455 90 : GSBGDataset::~GSBGDataset()
456 :
457 : {
458 45 : FlushCache(true);
459 45 : if (fp != nullptr)
460 45 : VSIFCloseL(fp);
461 90 : }
462 :
463 : /************************************************************************/
464 : /* Identify() */
465 : /************************************************************************/
466 :
467 52968 : int GSBGDataset::Identify(GDALOpenInfo *poOpenInfo)
468 :
469 : {
470 : /* Check for signature */
471 52968 : if (poOpenInfo->nHeaderBytes < 4 ||
472 4573 : !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "DSBB"))
473 : {
474 52878 : return FALSE;
475 : }
476 :
477 90 : return TRUE;
478 : }
479 :
480 : /************************************************************************/
481 : /* Open() */
482 : /************************************************************************/
483 :
484 45 : GDALDataset *GSBGDataset::Open(GDALOpenInfo *poOpenInfo)
485 :
486 : {
487 45 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
488 : {
489 0 : return nullptr;
490 : }
491 :
492 : /* -------------------------------------------------------------------- */
493 : /* Create a corresponding GDALDataset. */
494 : /* -------------------------------------------------------------------- */
495 90 : auto poDS = std::make_unique<GSBGDataset>();
496 :
497 45 : poDS->eAccess = poOpenInfo->eAccess;
498 45 : poDS->fp = poOpenInfo->fpL;
499 45 : poOpenInfo->fpL = nullptr;
500 :
501 : /* -------------------------------------------------------------------- */
502 : /* Read the header. */
503 : /* -------------------------------------------------------------------- */
504 45 : if (VSIFSeekL(poDS->fp, 4, SEEK_SET) != 0)
505 : {
506 0 : CPLError(CE_Failure, CPLE_FileIO,
507 : "Unable to seek to start of grid file header.\n");
508 0 : return nullptr;
509 : }
510 :
511 : /* Parse number of X axis grid rows */
512 : GInt16 nTemp;
513 45 : if (VSIFReadL((void *)&nTemp, 2, 1, poDS->fp) != 1)
514 : {
515 0 : CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster X size.\n");
516 0 : return nullptr;
517 : }
518 45 : poDS->nRasterXSize = CPL_LSBWORD16(nTemp);
519 :
520 45 : if (VSIFReadL((void *)&nTemp, 2, 1, poDS->fp) != 1)
521 : {
522 0 : CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster Y size.\n");
523 0 : return nullptr;
524 : }
525 45 : poDS->nRasterYSize = CPL_LSBWORD16(nTemp);
526 :
527 45 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
528 : {
529 0 : return nullptr;
530 : }
531 :
532 : /* -------------------------------------------------------------------- */
533 : /* Create band information objects. */
534 : /* -------------------------------------------------------------------- */
535 45 : GSBGRasterBand *poBand = new GSBGRasterBand(poDS.get(), 1);
536 45 : poDS->SetBand(1, poBand);
537 :
538 : double dfTemp;
539 45 : if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
540 : {
541 0 : CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum X value.\n");
542 0 : return nullptr;
543 : }
544 45 : CPL_LSBPTR64(&dfTemp);
545 45 : poBand->dfMinX = dfTemp;
546 :
547 45 : if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
548 : {
549 0 : CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum X value.\n");
550 0 : return nullptr;
551 : }
552 45 : CPL_LSBPTR64(&dfTemp);
553 45 : poBand->dfMaxX = dfTemp;
554 :
555 45 : if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
556 : {
557 0 : CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum Y value.\n");
558 0 : return nullptr;
559 : }
560 45 : CPL_LSBPTR64(&dfTemp);
561 45 : poBand->dfMinY = dfTemp;
562 :
563 45 : if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
564 : {
565 0 : CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum Y value.\n");
566 0 : return nullptr;
567 : }
568 45 : CPL_LSBPTR64(&dfTemp);
569 45 : poBand->dfMaxY = dfTemp;
570 :
571 45 : if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
572 : {
573 0 : CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum Z value.\n");
574 0 : return nullptr;
575 : }
576 45 : CPL_LSBPTR64(&dfTemp);
577 45 : poBand->dfMinZ = dfTemp;
578 :
579 45 : if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
580 : {
581 0 : CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum Z value.\n");
582 0 : return nullptr;
583 : }
584 45 : CPL_LSBPTR64(&dfTemp);
585 45 : poBand->dfMaxZ = dfTemp;
586 :
587 : /* -------------------------------------------------------------------- */
588 : /* Initialize any PAM information. */
589 : /* -------------------------------------------------------------------- */
590 45 : poDS->SetDescription(poOpenInfo->pszFilename);
591 45 : poDS->TryLoadXML();
592 :
593 : /* -------------------------------------------------------------------- */
594 : /* Check for external overviews. */
595 : /* -------------------------------------------------------------------- */
596 90 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename,
597 45 : poOpenInfo->GetSiblingFiles());
598 :
599 45 : return poDS.release();
600 : }
601 :
602 : /************************************************************************/
603 : /* GetGeoTransform() */
604 : /************************************************************************/
605 :
606 29 : CPLErr GSBGDataset::GetGeoTransform(double *padfGeoTransform)
607 : {
608 29 : if (padfGeoTransform == nullptr)
609 0 : return CE_Failure;
610 :
611 29 : GSBGRasterBand *poGRB = cpl::down_cast<GSBGRasterBand *>(GetRasterBand(1));
612 :
613 : /* check if we have a PAM GeoTransform stored */
614 29 : CPLPushErrorHandler(CPLQuietErrorHandler);
615 29 : CPLErr eErr = GDALPamDataset::GetGeoTransform(padfGeoTransform);
616 29 : CPLPopErrorHandler();
617 :
618 29 : if (eErr == CE_None)
619 0 : return CE_None;
620 :
621 29 : if (nRasterXSize == 1 || nRasterYSize == 1)
622 0 : return CE_Failure;
623 :
624 : /* calculate pixel size first */
625 29 : padfGeoTransform[1] = (poGRB->dfMaxX - poGRB->dfMinX) / (nRasterXSize - 1);
626 29 : padfGeoTransform[5] = (poGRB->dfMinY - poGRB->dfMaxY) / (nRasterYSize - 1);
627 :
628 : /* then calculate image origin */
629 29 : padfGeoTransform[0] = poGRB->dfMinX - padfGeoTransform[1] / 2;
630 29 : padfGeoTransform[3] = poGRB->dfMaxY - padfGeoTransform[5] / 2;
631 :
632 : /* tilt/rotation does not supported by the GS grids */
633 29 : padfGeoTransform[4] = 0.0;
634 29 : padfGeoTransform[2] = 0.0;
635 :
636 29 : return CE_None;
637 : }
638 :
639 : /************************************************************************/
640 : /* SetGeoTransform() */
641 : /************************************************************************/
642 :
643 12 : CPLErr GSBGDataset::SetGeoTransform(double *padfGeoTransform)
644 : {
645 12 : if (eAccess == GA_ReadOnly)
646 : {
647 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
648 : "Unable to set GeoTransform, dataset opened read only.\n");
649 0 : return CE_Failure;
650 : }
651 :
652 12 : GSBGRasterBand *poGRB = cpl::down_cast<GSBGRasterBand *>(GetRasterBand(1));
653 :
654 12 : if (padfGeoTransform == nullptr)
655 0 : return CE_Failure;
656 :
657 : /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */
658 : // CPLErr eErr = CE_None;
659 : /*if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0
660 : || padfGeoTransform[1] < 0.0 || padfGeoTransform[5] < 0.0 )
661 : eErr = GDALPamDataset::SetGeoTransform( padfGeoTransform );
662 :
663 : if( eErr != CE_None )
664 : return eErr;*/
665 :
666 12 : double dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2;
667 12 : double dfMaxX =
668 12 : padfGeoTransform[1] * (nRasterXSize - 0.5) + padfGeoTransform[0];
669 12 : double dfMinY =
670 12 : padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3];
671 12 : double dfMaxY = padfGeoTransform[3] + padfGeoTransform[5] / 2;
672 :
673 : CPLErr eErr =
674 12 : WriteHeader(fp, poGRB->nRasterXSize, poGRB->nRasterYSize, dfMinX,
675 : dfMaxX, dfMinY, dfMaxY, poGRB->dfMinZ, poGRB->dfMaxZ);
676 :
677 12 : if (eErr == CE_None)
678 : {
679 12 : poGRB->dfMinX = dfMinX;
680 12 : poGRB->dfMaxX = dfMaxX;
681 12 : poGRB->dfMinY = dfMinY;
682 12 : poGRB->dfMaxY = dfMaxY;
683 : }
684 :
685 12 : return eErr;
686 : }
687 :
688 : /************************************************************************/
689 : /* WriteHeader() */
690 : /************************************************************************/
691 :
692 65 : CPLErr GSBGDataset::WriteHeader(VSILFILE *fp, int nXSize, int nYSize,
693 : double dfMinX, double dfMaxX, double dfMinY,
694 : double dfMaxY, double dfMinZ, double dfMaxZ)
695 :
696 : {
697 65 : if (VSIFSeekL(fp, 0, SEEK_SET) != 0)
698 : {
699 0 : CPLError(CE_Failure, CPLE_FileIO,
700 : "Unable to seek to start of grid file.\n");
701 0 : return CE_Failure;
702 : }
703 :
704 65 : if (VSIFWriteL((void *)"DSBB", 1, 4, fp) != 4)
705 : {
706 1 : CPLError(CE_Failure, CPLE_FileIO,
707 : "Unable to write signature to grid file.\n");
708 1 : return CE_Failure;
709 : }
710 :
711 64 : assert(nXSize >= 0 && nXSize <= std::numeric_limits<int16_t>::max());
712 64 : GInt16 nTemp = CPL_LSBWORD16(static_cast<int16_t>(nXSize));
713 64 : if (VSIFWriteL((void *)&nTemp, 2, 1, fp) != 1)
714 : {
715 0 : CPLError(CE_Failure, CPLE_FileIO,
716 : "Unable to write raster X size to grid file.\n");
717 0 : return CE_Failure;
718 : }
719 :
720 64 : assert(nYSize >= 0 && nYSize <= std::numeric_limits<int16_t>::max());
721 64 : nTemp = CPL_LSBWORD16(static_cast<int16_t>(nYSize));
722 64 : if (VSIFWriteL((void *)&nTemp, 2, 1, fp) != 1)
723 : {
724 0 : CPLError(CE_Failure, CPLE_FileIO,
725 : "Unable to write raster Y size to grid file.\n");
726 0 : return CE_Failure;
727 : }
728 :
729 64 : double dfTemp = dfMinX;
730 64 : CPL_LSBPTR64(&dfTemp);
731 64 : if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
732 : {
733 0 : CPLError(CE_Failure, CPLE_FileIO,
734 : "Unable to write minimum X value to grid file.\n");
735 0 : return CE_Failure;
736 : }
737 :
738 64 : dfTemp = dfMaxX;
739 64 : CPL_LSBPTR64(&dfTemp);
740 64 : if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
741 : {
742 0 : CPLError(CE_Failure, CPLE_FileIO,
743 : "Unable to write maximum X value to grid file.\n");
744 0 : return CE_Failure;
745 : }
746 :
747 64 : dfTemp = dfMinY;
748 64 : CPL_LSBPTR64(&dfTemp);
749 64 : if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
750 : {
751 0 : CPLError(CE_Failure, CPLE_FileIO,
752 : "Unable to write minimum Y value to grid file.\n");
753 0 : return CE_Failure;
754 : }
755 :
756 64 : dfTemp = dfMaxY;
757 64 : CPL_LSBPTR64(&dfTemp);
758 64 : if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
759 : {
760 0 : CPLError(CE_Failure, CPLE_FileIO,
761 : "Unable to write maximum Y value to grid file.\n");
762 0 : return CE_Failure;
763 : }
764 :
765 64 : dfTemp = dfMinZ;
766 64 : CPL_LSBPTR64(&dfTemp);
767 64 : if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
768 : {
769 1 : CPLError(CE_Failure, CPLE_FileIO,
770 : "Unable to write minimum Z value to grid file.\n");
771 1 : return CE_Failure;
772 : }
773 :
774 63 : dfTemp = dfMaxZ;
775 63 : CPL_LSBPTR64(&dfTemp);
776 63 : if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
777 : {
778 0 : CPLError(CE_Failure, CPLE_FileIO,
779 : "Unable to write maximum Z value to grid file.\n");
780 0 : return CE_Failure;
781 : }
782 :
783 63 : return CE_None;
784 : }
785 :
786 : /************************************************************************/
787 : /* Create() */
788 : /************************************************************************/
789 :
790 33 : GDALDataset *GSBGDataset::Create(const char *pszFilename, int nXSize,
791 : int nYSize, int /* nBands */,
792 : GDALDataType eType,
793 : CPL_UNUSED char **papszParamList)
794 : {
795 33 : if (nXSize <= 0 || nYSize <= 0)
796 : {
797 0 : CPLError(CE_Failure, CPLE_IllegalArg,
798 : "Unable to create grid, both X and Y size must be "
799 : "non-negative.\n");
800 :
801 0 : return nullptr;
802 : }
803 66 : else if (nXSize > std::numeric_limits<short>::max() ||
804 33 : nYSize > std::numeric_limits<short>::max())
805 : {
806 0 : CPLError(CE_Failure, CPLE_IllegalArg,
807 : "Unable to create grid, Golden Software Binary Grid format "
808 : "only supports sizes up to %dx%d. %dx%d not supported.\n",
809 0 : std::numeric_limits<short>::max(),
810 0 : std::numeric_limits<short>::max(), nXSize, nYSize);
811 :
812 0 : return nullptr;
813 : }
814 :
815 33 : if (eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 &&
816 : eType != GDT_Int16)
817 : {
818 20 : CPLError(CE_Failure, CPLE_AppDefined,
819 : "Golden Software Binary Grid only supports Byte, Int16, "
820 : "Uint16, and Float32 datatypes. Unable to create with "
821 : "type %s.\n",
822 : GDALGetDataTypeName(eType));
823 :
824 20 : return nullptr;
825 : }
826 :
827 13 : VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
828 :
829 13 : if (fp == nullptr)
830 : {
831 0 : CPLError(CE_Failure, CPLE_OpenFailed,
832 : "Attempt to create file '%s' failed.\n", pszFilename);
833 0 : return nullptr;
834 : }
835 :
836 : CPLErr eErr =
837 13 : WriteHeader(fp, nXSize, nYSize, 0.0, nXSize, 0.0, nYSize, 0.0, 0.0);
838 13 : if (eErr != CE_None)
839 : {
840 0 : VSIFCloseL(fp);
841 0 : return nullptr;
842 : }
843 :
844 13 : float fVal = fNODATA_VALUE;
845 13 : CPL_LSBPTR32(&fVal);
846 1233 : for (int iRow = 0; iRow < nYSize; iRow++)
847 : {
848 121620 : for (int iCol = 0; iCol < nXSize; iCol++)
849 : {
850 120400 : if (VSIFWriteL((void *)&fVal, 4, 1, fp) != 1)
851 : {
852 0 : VSIFCloseL(fp);
853 0 : CPLError(CE_Failure, CPLE_FileIO,
854 : "Unable to write grid cell. Disk full?\n");
855 0 : return nullptr;
856 : }
857 : }
858 : }
859 :
860 13 : VSIFCloseL(fp);
861 :
862 13 : return (GDALDataset *)GDALOpen(pszFilename, GA_Update);
863 : }
864 :
865 : /************************************************************************/
866 : /* CreateCopy() */
867 : /************************************************************************/
868 :
869 30 : GDALDataset *GSBGDataset::CreateCopy(const char *pszFilename,
870 : GDALDataset *poSrcDS, int bStrict,
871 : CPL_UNUSED char **papszOptions,
872 : GDALProgressFunc pfnProgress,
873 : void *pProgressData)
874 : {
875 30 : if (pfnProgress == nullptr)
876 0 : pfnProgress = GDALDummyProgress;
877 :
878 30 : int nBands = poSrcDS->GetRasterCount();
879 30 : if (nBands == 0)
880 : {
881 1 : CPLError(
882 : CE_Failure, CPLE_NotSupported,
883 : "GSBG driver does not support source dataset with zero band.\n");
884 1 : return nullptr;
885 : }
886 29 : else if (nBands > 1)
887 : {
888 4 : if (bStrict)
889 : {
890 4 : CPLError(CE_Failure, CPLE_NotSupported,
891 : "Unable to create copy, Golden Software Binary Grid "
892 : "format only supports one raster band.\n");
893 4 : return nullptr;
894 : }
895 : else
896 0 : CPLError(CE_Warning, CPLE_NotSupported,
897 : "Golden Software Binary Grid format only supports one "
898 : "raster band, first band will be copied.\n");
899 : }
900 :
901 25 : GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
902 50 : if (poSrcBand->GetXSize() > std::numeric_limits<short>::max() ||
903 25 : poSrcBand->GetYSize() > std::numeric_limits<short>::max())
904 : {
905 0 : CPLError(CE_Failure, CPLE_IllegalArg,
906 : "Unable to create grid, Golden Software Binary Grid format "
907 : "only supports sizes up to %dx%d. %dx%d not supported.\n",
908 0 : std::numeric_limits<short>::max(),
909 0 : std::numeric_limits<short>::max(), poSrcBand->GetXSize(),
910 : poSrcBand->GetYSize());
911 :
912 0 : return nullptr;
913 : }
914 :
915 25 : if (!pfnProgress(0.0, nullptr, pProgressData))
916 : {
917 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated\n");
918 0 : return nullptr;
919 : }
920 :
921 25 : VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
922 :
923 25 : if (fp == nullptr)
924 : {
925 3 : CPLError(CE_Failure, CPLE_OpenFailed,
926 : "Attempt to create file '%s' failed.\n", pszFilename);
927 3 : return nullptr;
928 : }
929 :
930 22 : const int nXSize = poSrcBand->GetXSize();
931 22 : const int nYSize = poSrcBand->GetYSize();
932 : double adfGeoTransform[6];
933 :
934 22 : poSrcDS->GetGeoTransform(adfGeoTransform);
935 :
936 22 : double dfMinX = adfGeoTransform[0] + adfGeoTransform[1] / 2;
937 22 : double dfMaxX = adfGeoTransform[1] * (nXSize - 0.5) + adfGeoTransform[0];
938 22 : double dfMinY = adfGeoTransform[5] * (nYSize - 0.5) + adfGeoTransform[3];
939 22 : double dfMaxY = adfGeoTransform[3] + adfGeoTransform[5] / 2;
940 22 : CPLErr eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY,
941 : dfMaxY, 0.0, 0.0);
942 :
943 22 : if (eErr != CE_None)
944 : {
945 2 : VSIFCloseL(fp);
946 2 : return nullptr;
947 : }
948 :
949 : /* -------------------------------------------------------------------- */
950 : /* Copy band data. */
951 : /* -------------------------------------------------------------------- */
952 20 : float *pfData = (float *)VSI_MALLOC2_VERBOSE(nXSize, sizeof(float));
953 20 : if (pfData == nullptr)
954 : {
955 0 : VSIFCloseL(fp);
956 0 : return nullptr;
957 : }
958 :
959 : int bSrcHasNDValue;
960 20 : float fSrcNoDataValue = (float)poSrcBand->GetNoDataValue(&bSrcHasNDValue);
961 20 : double dfMinZ = std::numeric_limits<double>::max();
962 20 : double dfMaxZ = std::numeric_limits<double>::lowest();
963 185 : for (int iRow = nYSize - 1; iRow >= 0; iRow--)
964 : {
965 173 : eErr = poSrcBand->RasterIO(GF_Read, 0, iRow, nXSize, 1, pfData, nXSize,
966 : 1, GDT_Float32, 0, 0, nullptr);
967 :
968 173 : if (eErr != CE_None)
969 : {
970 0 : VSIFCloseL(fp);
971 0 : VSIFree(pfData);
972 0 : return nullptr;
973 : }
974 :
975 2103 : for (int iCol = 0; iCol < nXSize; iCol++)
976 : {
977 1930 : if (bSrcHasNDValue && pfData[iCol] == fSrcNoDataValue)
978 : {
979 0 : pfData[iCol] = fNODATA_VALUE;
980 : }
981 : else
982 : {
983 1930 : if (pfData[iCol] > dfMaxZ)
984 22 : dfMaxZ = pfData[iCol];
985 :
986 1930 : if (pfData[iCol] < dfMinZ)
987 27 : dfMinZ = pfData[iCol];
988 : }
989 :
990 1930 : CPL_LSBPTR32(pfData + iCol);
991 : }
992 :
993 173 : if (VSIFWriteL((void *)pfData, 4, nXSize, fp) !=
994 173 : static_cast<unsigned>(nXSize))
995 : {
996 8 : VSIFCloseL(fp);
997 8 : VSIFree(pfData);
998 8 : CPLError(CE_Failure, CPLE_FileIO,
999 : "Unable to write grid row. Disk full?\n");
1000 8 : return nullptr;
1001 : }
1002 :
1003 165 : if (!pfnProgress(static_cast<double>(nYSize - iRow) / nYSize, nullptr,
1004 : pProgressData))
1005 : {
1006 0 : VSIFCloseL(fp);
1007 0 : VSIFree(pfData);
1008 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1009 0 : return nullptr;
1010 : }
1011 : }
1012 :
1013 12 : VSIFree(pfData);
1014 :
1015 : /* write out the min and max values */
1016 12 : eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY, dfMaxY,
1017 : dfMinZ, dfMaxZ);
1018 :
1019 12 : if (eErr != CE_None)
1020 : {
1021 0 : VSIFCloseL(fp);
1022 0 : return nullptr;
1023 : }
1024 :
1025 12 : VSIFCloseL(fp);
1026 :
1027 12 : GDALPamDataset *poDS = (GDALPamDataset *)GDALOpen(pszFilename, GA_Update);
1028 12 : if (poDS)
1029 : {
1030 12 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
1031 : }
1032 12 : return poDS;
1033 : }
1034 :
1035 : /************************************************************************/
1036 : /* GDALRegister_GSBG() */
1037 : /************************************************************************/
1038 :
1039 1595 : void GDALRegister_GSBG()
1040 :
1041 : {
1042 1595 : if (GDALGetDriverByName("GSBG") != nullptr)
1043 302 : return;
1044 :
1045 1293 : GDALDriver *poDriver = new GDALDriver();
1046 :
1047 1293 : poDriver->SetDescription("GSBG");
1048 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1049 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1050 1293 : "Golden Software Binary Grid (.grd)");
1051 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gsbg.html");
1052 1293 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd");
1053 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1054 1293 : "Byte Int16 UInt16 Float32");
1055 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1056 :
1057 1293 : poDriver->pfnIdentify = GSBGDataset::Identify;
1058 1293 : poDriver->pfnOpen = GSBGDataset::Open;
1059 1293 : poDriver->pfnCreate = GSBGDataset::Create;
1060 1293 : poDriver->pfnCreateCopy = GSBGDataset::CreateCopy;
1061 :
1062 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
1063 : }
|