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