Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GRD Reader
4 : * Purpose: GDAL driver for Northwood Grid Format
5 : * Author: Perry Casson
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2006, Waypoint Information Technology
9 : * Copyright (c) 2009-2012, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <string>
15 : #include <cstring>
16 : #include <cstdio>
17 : #include <climits>
18 : #include "gdal_frmts.h"
19 : #include "gdal_pam.h"
20 : #include "gdal_driver.h"
21 : #include "gdal_drivermanager.h"
22 : #include "gdal_openinfo.h"
23 : #include "gdal_cpp_functions.h"
24 : #include "northwood.h"
25 : #include "ogrmitabspatialref.h"
26 :
27 : #ifndef NO_MITAB_SUPPORT
28 : #ifdef MSVC
29 : #include "..\..\ogr\ogrsf_frmts\mitab\mitab.h"
30 : #else
31 : #include "../../ogr/ogrsf_frmts/mitab/mitab.h"
32 : #endif
33 : #endif
34 :
35 : constexpr float NODATA = -1.e37f;
36 : constexpr double SCALE16BIT = 65534.0;
37 : constexpr double SCALE32BIT = 4294967294.0;
38 :
39 : void replaceExt(std::string &s, const std::string &newExt);
40 :
41 : /************************************************************************/
42 : /* Replace the extension on a filepath with an alternative extension */
43 : /************************************************************************/
44 0 : void replaceExt(std::string &s, const std::string &newExt)
45 : {
46 :
47 0 : std::string::size_type i = s.rfind('.', s.length());
48 :
49 0 : if (i != std::string::npos)
50 : {
51 0 : s.replace(i + 1, newExt.length(), newExt);
52 : }
53 0 : }
54 :
55 : /************************************************************************/
56 : /* ==================================================================== */
57 : /* NWT_GRDDataset */
58 : /* ==================================================================== */
59 : /************************************************************************/
60 : class NWT_GRDRasterBand;
61 :
62 : class NWT_GRDDataset final : public GDALPamDataset
63 : {
64 : friend class NWT_GRDRasterBand;
65 :
66 : VSILFILE *fp;
67 : GByte abyHeader[1024];
68 : NWT_GRID *pGrd;
69 : NWT_RGB ColorMap[4096];
70 : bool bUpdateHeader;
71 : mutable OGRSpatialReference *m_poSRS = nullptr;
72 :
73 : #ifndef NO_MITAB_SUPPORT
74 : // Update the header data with latest changes
75 : int UpdateHeader();
76 : int WriteTab();
77 : #endif
78 :
79 : NWT_GRDDataset(const NWT_GRDDataset &) = delete;
80 : NWT_GRDDataset &operator=(const NWT_GRDDataset &) = delete;
81 :
82 : public:
83 : NWT_GRDDataset();
84 : ~NWT_GRDDataset() override;
85 :
86 : static GDALDataset *Open(GDALOpenInfo *);
87 : static int Identify(GDALOpenInfo *);
88 :
89 : #ifndef NO_MITAB_SUPPORT
90 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
91 : int nBandsIn, GDALDataType eType,
92 : char **papszParamList);
93 : static GDALDataset *CreateCopy(const char *pszFilename,
94 : GDALDataset *poSrcDS, int bStrict,
95 : char **papszOptions,
96 : GDALProgressFunc pfnProgress,
97 : void *pProgressData);
98 : #endif
99 :
100 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
101 : CPLErr SetGeoTransform(const GDALGeoTransform >) override;
102 : CPLErr FlushCache(bool bAtClosing) override;
103 :
104 : const OGRSpatialReference *GetSpatialRef() const override;
105 :
106 : #ifndef NO_MITAB_SUPPORT
107 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
108 : #endif
109 : };
110 :
111 : /************************************************************************/
112 : /* ==================================================================== */
113 : /* NWT_GRDRasterBand */
114 : /* ==================================================================== */
115 : /************************************************************************/
116 :
117 : class NWT_GRDRasterBand final : public GDALPamRasterBand
118 : {
119 : friend class NWT_GRDDataset;
120 :
121 : int bHaveOffsetScale;
122 : double dfOffset;
123 : double dfScale;
124 : double dfNoData;
125 :
126 : public:
127 : NWT_GRDRasterBand(NWT_GRDDataset *, int, int);
128 :
129 : CPLErr IReadBlock(int, int, void *) override;
130 : CPLErr IWriteBlock(int, int, void *) override;
131 : double GetNoDataValue(int *pbSuccess) override;
132 : CPLErr SetNoDataValue(double dfNoData) override;
133 :
134 : GDALColorInterp GetColorInterpretation() override;
135 : };
136 :
137 : /************************************************************************/
138 : /* NWT_GRDRasterBand() */
139 : /************************************************************************/
140 45 : NWT_GRDRasterBand::NWT_GRDRasterBand(NWT_GRDDataset *poDSIn, int nBandIn,
141 45 : int nBands)
142 45 : : bHaveOffsetScale(FALSE), dfOffset(0.0), dfScale(1.0), dfNoData(0.0)
143 : {
144 45 : poDS = poDSIn;
145 45 : nBand = nBandIn;
146 :
147 : /*
148 : * If nBand = 4 we have opened in read mode and have created the 3 'virtual'
149 : * RGB bands. so the 4th band is the actual data Otherwise, if we have
150 : * opened in update mode, there is only 1 band, which is the actual data
151 : */
152 45 : if (nBand == 4 || nBands == 1)
153 : {
154 15 : bHaveOffsetScale = TRUE;
155 15 : dfOffset = poDSIn->pGrd->fZMin;
156 :
157 15 : if (poDSIn->pGrd->cFormat == 0x00)
158 : {
159 15 : eDataType = GDT_Float32;
160 15 : dfScale = (poDSIn->pGrd->fZMax - poDSIn->pGrd->fZMin) / SCALE16BIT;
161 : }
162 : else
163 : {
164 0 : eDataType = GDT_Float32;
165 0 : dfScale = (poDSIn->pGrd->fZMax - poDSIn->pGrd->fZMin) / SCALE32BIT;
166 : }
167 : }
168 : else
169 : {
170 30 : bHaveOffsetScale = FALSE;
171 30 : dfOffset = 0;
172 30 : dfScale = 1.0;
173 30 : eDataType = GDT_Byte;
174 : }
175 45 : nBlockXSize = poDS->GetRasterXSize();
176 45 : nBlockYSize = 1;
177 45 : }
178 :
179 148 : double NWT_GRDRasterBand::GetNoDataValue(int *pbSuccess)
180 : {
181 148 : NWT_GRDDataset *poGDS = cpl::down_cast<NWT_GRDDataset *>(poDS);
182 : double dRetval;
183 148 : if ((nBand == 4) || (poGDS->nBands == 1))
184 : {
185 148 : if (pbSuccess != nullptr)
186 147 : *pbSuccess = TRUE;
187 148 : if (dfNoData != 0.0)
188 : {
189 0 : dRetval = dfNoData;
190 : }
191 : else
192 : {
193 148 : dRetval = NODATA;
194 : }
195 :
196 148 : return dRetval;
197 : }
198 :
199 0 : if (pbSuccess != nullptr)
200 0 : *pbSuccess = FALSE;
201 :
202 0 : return 0;
203 : }
204 :
205 1 : CPLErr NWT_GRDRasterBand::SetNoDataValue(double dfNoDataIn)
206 : {
207 : // This is essentially a 'virtual' no data value.
208 : // Once set, when writing an value == dfNoData will
209 : // be converted to the no data value (0) on disk.
210 : // If opened again; the no data value will always be the
211 : // default (-1.e37f)
212 1 : dfNoData = dfNoDataIn;
213 1 : return CE_None;
214 : }
215 :
216 5 : GDALColorInterp NWT_GRDRasterBand::GetColorInterpretation()
217 : {
218 5 : NWT_GRDDataset *poGDS = cpl::down_cast<NWT_GRDDataset *>(poDS);
219 : // return GCI_RGB;
220 5 : if ((nBand == 4) || (poGDS->nBands == 1))
221 5 : return GCI_GrayIndex;
222 0 : else if (nBand == 1)
223 0 : return GCI_RedBand;
224 0 : else if (nBand == 2)
225 0 : return GCI_GreenBand;
226 0 : else if (nBand == 3)
227 0 : return GCI_BlueBand;
228 :
229 0 : return GCI_Undefined;
230 : }
231 :
232 : /************************************************************************/
233 : /* IWriteBlock() */
234 : /************************************************************************/
235 48 : CPLErr NWT_GRDRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
236 : void *pImage)
237 : {
238 :
239 : // Each block is an entire row of the dataset, so the x offset should always
240 : // be 0
241 48 : CPLAssert(nBlockXOff == 0);
242 48 : NWT_GRDDataset *poGDS = cpl::down_cast<NWT_GRDDataset *>(poDS);
243 :
244 48 : if (dfScale == 0.0)
245 1 : return CE_Failure;
246 :
247 : // Ensure the blocksize is not beyond the system limits and
248 : // initialize the size of the record
249 47 : if (nBlockXSize > INT_MAX / 2)
250 : {
251 0 : return CE_Failure;
252 : }
253 47 : const int nRecordSize = nBlockXSize * 2;
254 :
255 : // Seek to the write position in the GRD file
256 47 : VSIFSeekL(poGDS->fp,
257 47 : 1024 + nRecordSize * static_cast<vsi_l_offset>(nBlockYOff),
258 : SEEK_SET);
259 :
260 : // Cast pImage to float
261 47 : const float *pfImage = static_cast<const float *>(pImage);
262 :
263 : // Initialize output array
264 47 : GByte *pabyRecord = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nRecordSize));
265 47 : if (pabyRecord == nullptr)
266 0 : return CE_Failure;
267 :
268 : // We only ever write to band 4; RGB bands are basically 'virtual'
269 : // (i.e. the RGB colour is computed from the raw data).
270 : // For all intents and purposes, there is essentially 1 band on disk.
271 47 : if (nBand == 1)
272 : {
273 3008 : for (int i = 0; i < nBlockXSize; i++)
274 : {
275 2961 : const float fValue = pfImage[i];
276 : unsigned short nWrite; // The stretched value to be written
277 :
278 : // We allow data to be interpreted by a user-defined null value
279 : // (a 'virtual' value, since it is always 0 on disk) or
280 : // if not defined we default to the GRD standard of -1E37.
281 : // We allow a little bit of flexibility in that if it is below -1E37
282 : // it is in all probability still intended as a null value.
283 2961 : if ((fValue == dfNoData) || (fValue <= NODATA))
284 : {
285 381 : nWrite = 0;
286 : }
287 : else
288 : {
289 2580 : if (fValue < poGDS->pGrd->fZMin)
290 : {
291 0 : poGDS->pGrd->fZMin = fValue;
292 : }
293 2580 : else if (fValue > poGDS->pGrd->fZMax)
294 : {
295 0 : poGDS->pGrd->fZMax = fValue;
296 : }
297 : // Data on disk is stretched within the unsigned short range so
298 : // we must convert (the inverse of what is done in IReadBlock),
299 : // based on the Z value range
300 2580 : nWrite = static_cast<unsigned short>(
301 2580 : ((fValue - dfOffset) / dfScale) + 1);
302 : }
303 2961 : CPL_LSBPTR16(&nWrite);
304 : // Copy the result to the byte array (2 bytes per value)
305 2961 : memcpy(pabyRecord + 2 * i, &nWrite, 2);
306 : }
307 :
308 : // Write the buffer to disk
309 47 : if (VSIFWriteL(pabyRecord, 1, nRecordSize, poGDS->fp) !=
310 47 : static_cast<size_t>(nRecordSize))
311 : {
312 0 : CPLError(CE_Failure, CPLE_FileIO,
313 : "Failed to write scanline %d to file.\n", nBlockYOff);
314 0 : CPLFree(pabyRecord);
315 0 : return CE_Failure;
316 : }
317 : }
318 : else
319 : {
320 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Writing to band %d is not valid",
321 : nBand);
322 0 : CPLFree(pabyRecord);
323 0 : return CE_Failure;
324 : }
325 47 : CPLFree(pabyRecord);
326 47 : return CE_None;
327 : }
328 :
329 : /************************************************************************/
330 : /* IReadBlock() */
331 : /************************************************************************/
332 282 : CPLErr NWT_GRDRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
333 : void *pImage)
334 : {
335 282 : NWT_GRDDataset *poGDS = cpl::down_cast<NWT_GRDDataset *>(poDS);
336 282 : if (nBlockXSize > INT_MAX / 2)
337 0 : return CE_Failure;
338 282 : const int nRecordSize = nBlockXSize * 2;
339 :
340 : // Seek to the data position
341 282 : VSIFSeekL(poGDS->fp,
342 282 : 1024 + nRecordSize * static_cast<vsi_l_offset>(nBlockYOff),
343 : SEEK_SET);
344 :
345 282 : GByte *pabyRecord = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nRecordSize));
346 282 : if (pabyRecord == nullptr)
347 0 : return CE_Failure;
348 :
349 : // Read the data
350 282 : if (static_cast<int>(VSIFReadL(pabyRecord, 1, nRecordSize, poGDS->fp)) !=
351 : nRecordSize)
352 : {
353 0 : CPLFree(pabyRecord);
354 0 : return CE_Failure;
355 : }
356 :
357 282 : if ((nBand == 4) || (poGDS->nBands == 1)) // Z values
358 : {
359 : int bSuccess;
360 141 : const float fNoData = static_cast<float>(GetNoDataValue(&bSuccess));
361 9024 : for (int i = 0; i < nBlockXSize; i++)
362 : {
363 : unsigned short raw1;
364 8883 : memcpy(&raw1, pabyRecord + 2 * i, 2);
365 8883 : CPL_LSBPTR16(&raw1);
366 8883 : if (raw1 == 0)
367 : {
368 1143 : static_cast<float *>(pImage)[i] = fNoData; // null value
369 : }
370 : else
371 : {
372 7740 : static_cast<float *>(pImage)[i] =
373 7740 : static_cast<float>(dfOffset + ((raw1 - 1) * dfScale));
374 : }
375 141 : }
376 : }
377 141 : else if (nBand == 1) // red values
378 : {
379 3008 : for (int i = 0; i < nBlockXSize; i++)
380 : {
381 : unsigned short raw1;
382 2961 : memcpy(&raw1, pabyRecord + 2 * i, 2);
383 2961 : CPL_LSBPTR16(&raw1);
384 2961 : static_cast<unsigned char *>(pImage)[i] =
385 2961 : poGDS->ColorMap[raw1 / 16].r;
386 : }
387 : }
388 94 : else if (nBand == 2) // green
389 : {
390 3008 : for (int i = 0; i < nBlockXSize; i++)
391 : {
392 : unsigned short raw1;
393 2961 : memcpy(&raw1, pabyRecord + 2 * i, 2);
394 2961 : CPL_LSBPTR16(&raw1);
395 2961 : static_cast<unsigned char *>(pImage)[i] =
396 2961 : poGDS->ColorMap[raw1 / 16].g;
397 : }
398 : }
399 47 : else if (nBand == 3) // blue
400 : {
401 3008 : for (int i = 0; i < nBlockXSize; i++)
402 : {
403 : unsigned short raw1;
404 2961 : memcpy(&raw1, pabyRecord + 2 * i, 2);
405 2961 : CPL_LSBPTR16(&raw1);
406 2961 : static_cast<unsigned char *>(pImage)[i] =
407 2961 : poGDS->ColorMap[raw1 / 16].b;
408 : }
409 : }
410 : else
411 : {
412 0 : CPLError(CE_Failure, CPLE_IllegalArg, "No band number %d", nBand);
413 0 : CPLFree(pabyRecord);
414 0 : return CE_Failure;
415 : }
416 :
417 282 : CPLFree(pabyRecord);
418 :
419 282 : return CE_None;
420 : }
421 :
422 : /************************************************************************/
423 : /* ==================================================================== */
424 : /* NWT_GRDDataset */
425 : /* ==================================================================== */
426 : /************************************************************************/
427 :
428 26 : NWT_GRDDataset::NWT_GRDDataset()
429 26 : : fp(nullptr), pGrd(nullptr), bUpdateHeader(false)
430 : {
431 : // poCT = NULL;
432 106522 : for (size_t i = 0; i < CPL_ARRAYSIZE(ColorMap); ++i)
433 : {
434 106496 : ColorMap[i].r = 0;
435 106496 : ColorMap[i].g = 0;
436 106496 : ColorMap[i].b = 0;
437 : }
438 26 : }
439 :
440 : /************************************************************************/
441 : /* ~NWT_GRDDataset() */
442 : /************************************************************************/
443 :
444 52 : NWT_GRDDataset::~NWT_GRDDataset()
445 : {
446 :
447 : // Make sure any changes to the header etc are written
448 : // if we are in update mode.
449 26 : if (eAccess == GA_Update)
450 : {
451 14 : NWT_GRDDataset::FlushCache(true);
452 : }
453 26 : if (pGrd)
454 : {
455 26 : pGrd->fp = nullptr; // this prevents nwtCloseGrid from closing the fp
456 26 : nwtCloseGrid(pGrd);
457 : }
458 26 : if (m_poSRS)
459 1 : m_poSRS->Release();
460 :
461 26 : if (fp != nullptr)
462 25 : VSIFCloseL(fp);
463 52 : }
464 :
465 : /************************************************************************/
466 : /* ~FlushCache(bool bAtClosing) */
467 : /************************************************************************/
468 17 : CPLErr NWT_GRDDataset::FlushCache(bool bAtClosing)
469 : {
470 : // Ensure the header and TAB file are up to date
471 17 : if (bUpdateHeader && pGrd)
472 : {
473 : #ifndef NO_MITAB_SUPPORT
474 3 : UpdateHeader();
475 : #endif
476 : }
477 :
478 : // Call the parent method
479 17 : return GDALPamDataset::FlushCache(bAtClosing);
480 : }
481 :
482 : /************************************************************************/
483 : /* GetGeoTransform() */
484 : /************************************************************************/
485 :
486 3 : CPLErr NWT_GRDDataset::GetGeoTransform(GDALGeoTransform >) const
487 : {
488 3 : gt[0] = pGrd->dfMinX - (pGrd->dfStepSize * 0.5);
489 3 : gt[3] = pGrd->dfMaxY + (pGrd->dfStepSize * 0.5);
490 3 : gt[1] = pGrd->dfStepSize;
491 3 : gt[2] = 0.0;
492 :
493 3 : gt[4] = 0.0;
494 3 : gt[5] = -1 * pGrd->dfStepSize;
495 :
496 3 : return CE_None;
497 : }
498 :
499 : /************************************************************************/
500 : /* SetGeoTransform() */
501 : /************************************************************************/
502 :
503 3 : CPLErr NWT_GRDDataset::SetGeoTransform(const GDALGeoTransform >)
504 : {
505 3 : if (gt[2] != 0.0 || gt[4] != 0.0)
506 : {
507 :
508 0 : CPLError(CE_Failure, CPLE_NotSupported,
509 : "GRD datasets do not support skew/rotation");
510 0 : return CE_Failure;
511 : }
512 3 : pGrd->dfStepSize = gt[1];
513 :
514 : // GRD format sets the min/max coordinates to the centre of the
515 : // cell; We must account for this when copying the GDAL geotransform
516 : // which references the top left corner
517 3 : pGrd->dfMinX = gt[0] + (pGrd->dfStepSize * 0.5);
518 3 : pGrd->dfMaxY = gt[3] - (pGrd->dfStepSize * 0.5);
519 :
520 : // Now set the miny and maxx
521 3 : pGrd->dfMaxX = pGrd->dfMinX + (pGrd->dfStepSize * (nRasterXSize - 1));
522 3 : pGrd->dfMinY = pGrd->dfMaxY - (pGrd->dfStepSize * (nRasterYSize - 1));
523 3 : bUpdateHeader = true;
524 :
525 3 : return CE_None;
526 : }
527 :
528 : /************************************************************************/
529 : /* GetSpatialRef() */
530 : /************************************************************************/
531 1 : const OGRSpatialReference *NWT_GRDDataset::GetSpatialRef() const
532 : {
533 :
534 : // First try getting it from the PAM dataset
535 1 : const OGRSpatialReference *poSRS = GDALPamDataset::GetSpatialRef();
536 1 : if (poSRS)
537 0 : return poSRS;
538 :
539 1 : if (m_poSRS)
540 0 : return m_poSRS;
541 :
542 : // If that isn't possible, read it from the GRD file. This may be a less
543 : // complete projection string.
544 : OGRSpatialReference *poSpatialRef =
545 1 : MITABCoordSys2SpatialRef(pGrd->cMICoordSys);
546 1 : m_poSRS = poSpatialRef;
547 :
548 1 : return m_poSRS;
549 : }
550 :
551 : #ifndef NO_MITAB_SUPPORT
552 : /************************************************************************/
553 : /* SetSpatialRef() */
554 : /************************************************************************/
555 :
556 3 : CPLErr NWT_GRDDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
557 : {
558 :
559 3 : char *psTABProj = MITABSpatialRef2CoordSys(poSRS);
560 3 : strncpy(pGrd->cMICoordSys, psTABProj, sizeof(pGrd->cMICoordSys) - 1);
561 3 : pGrd->cMICoordSys[255] = '\0';
562 :
563 : // Free temp projection.
564 3 : CPLFree(psTABProj);
565 : // Set projection in PAM dataset, so that
566 : // GDAL can always retrieve the complete projection.
567 3 : GDALPamDataset::SetSpatialRef(poSRS);
568 3 : bUpdateHeader = true;
569 :
570 3 : return CE_None;
571 : }
572 : #endif
573 :
574 : /************************************************************************/
575 : /* Identify() */
576 : /************************************************************************/
577 :
578 58532 : int NWT_GRDDataset::Identify(GDALOpenInfo *poOpenInfo)
579 : {
580 : /* -------------------------------------------------------------------- */
581 : /* Look for the header */
582 : /* -------------------------------------------------------------------- */
583 58532 : if (poOpenInfo->nHeaderBytes < 1024)
584 56588 : return FALSE;
585 :
586 1944 : if (poOpenInfo->pabyHeader[0] != 'H' || poOpenInfo->pabyHeader[1] != 'G' ||
587 27 : poOpenInfo->pabyHeader[2] != 'P' || poOpenInfo->pabyHeader[3] != 'C' ||
588 27 : poOpenInfo->pabyHeader[4] != '1')
589 1920 : return FALSE;
590 :
591 24 : return TRUE;
592 : }
593 :
594 : /************************************************************************/
595 : /* Open() */
596 : /************************************************************************/
597 12 : GDALDataset *NWT_GRDDataset::Open(GDALOpenInfo *poOpenInfo)
598 : {
599 12 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
600 0 : return nullptr;
601 :
602 : /* -------------------------------------------------------------------- */
603 : /* Create a corresponding GDALDataset. */
604 : /* -------------------------------------------------------------------- */
605 12 : int nBandsToCreate = 0;
606 :
607 12 : NWT_GRDDataset *poDS = new NWT_GRDDataset();
608 :
609 12 : poDS->fp = poOpenInfo->fpL;
610 12 : poOpenInfo->fpL = nullptr;
611 :
612 12 : if (poOpenInfo->eAccess == GA_Update)
613 : {
614 0 : nBandsToCreate = 1;
615 : }
616 : else
617 : {
618 12 : nBandsToCreate = atoi(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
619 : "BAND_COUNT", "4"));
620 12 : if (nBandsToCreate != 1 && nBandsToCreate != 4)
621 : {
622 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for BAND_COUNT");
623 0 : delete poDS;
624 0 : return nullptr;
625 : }
626 : }
627 12 : poDS->eAccess = poOpenInfo->eAccess;
628 :
629 : /* -------------------------------------------------------------------- */
630 : /* Read the header. */
631 : /* -------------------------------------------------------------------- */
632 12 : VSIFSeekL(poDS->fp, 0, SEEK_SET);
633 12 : VSIFReadL(poDS->abyHeader, 1, 1024, poDS->fp);
634 12 : poDS->pGrd = static_cast<NWT_GRID *>(calloc(1, sizeof(NWT_GRID)));
635 12 : if (!poDS->pGrd)
636 : {
637 0 : delete poDS;
638 0 : return nullptr;
639 : }
640 :
641 12 : poDS->pGrd->fp = poDS->fp;
642 :
643 24 : if (!nwt_ParseHeader(poDS->pGrd, poDS->abyHeader) ||
644 12 : !GDALCheckDatasetDimensions(poDS->pGrd->nXSide, poDS->pGrd->nYSide))
645 : {
646 0 : delete poDS;
647 0 : return nullptr;
648 : }
649 :
650 12 : poDS->nRasterXSize = poDS->pGrd->nXSide;
651 12 : poDS->nRasterYSize = poDS->pGrd->nYSide;
652 :
653 : // create a colorTable
654 : // if( poDS->pGrd->iNumColorInflections > 0 )
655 : // poDS->CreateColorTable();
656 12 : nwt_LoadColors(poDS->ColorMap, 4096, poDS->pGrd);
657 : /* -------------------------------------------------------------------- */
658 : /* Create band information objects. */
659 : /* If opening in read-only mode, then we create 4 bands (RGBZ) */
660 : /* with data values being available in band 4. If opening in update mode*/
661 : /* we create 1 band (the data values). This is because in reality, there*/
662 : /* is only 1 band stored on disk. The RGB bands are 'virtual' - derived */
663 : /* from the data values on the fly */
664 : /* -------------------------------------------------------------------- */
665 54 : for (int i = 0; i < nBandsToCreate; ++i)
666 : {
667 42 : poDS->SetBand(i + 1,
668 42 : new NWT_GRDRasterBand(poDS, i + 1, nBandsToCreate));
669 : }
670 :
671 : /* -------------------------------------------------------------------- */
672 : /* Initialize any PAM information. */
673 : /* -------------------------------------------------------------------- */
674 12 : poDS->SetDescription(poOpenInfo->pszFilename);
675 12 : poDS->TryLoadXML();
676 :
677 : /* -------------------------------------------------------------------- */
678 : /* Check for external overviews. */
679 : /* -------------------------------------------------------------------- */
680 24 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
681 12 : poOpenInfo->GetSiblingFiles());
682 :
683 12 : return poDS;
684 : }
685 :
686 : #ifndef NO_MITAB_SUPPORT
687 : /************************************************************************/
688 : /* UpdateHeader() */
689 : /************************************************************************/
690 16 : int NWT_GRDDataset::UpdateHeader()
691 : {
692 16 : int iStatus = 0;
693 16 : TABRawBinBlock *poHeaderBlock = new TABRawBinBlock(TABReadWrite, TRUE);
694 16 : poHeaderBlock->InitNewBlock(fp, 1024);
695 :
696 : // Write the header string
697 16 : poHeaderBlock->WriteBytes(5, reinterpret_cast<const GByte *>("HGPC1\0"));
698 :
699 : // Version number
700 16 : poHeaderBlock->WriteFloat(pGrd->fVersion);
701 :
702 : // Dimensions
703 16 : poHeaderBlock->WriteInt16(static_cast<GInt16>(pGrd->nXSide));
704 16 : poHeaderBlock->WriteInt16(static_cast<GInt16>(pGrd->nYSide));
705 :
706 : // Extents
707 16 : poHeaderBlock->WriteDouble(pGrd->dfMinX);
708 16 : poHeaderBlock->WriteDouble(pGrd->dfMaxX);
709 16 : poHeaderBlock->WriteDouble(pGrd->dfMinY);
710 16 : poHeaderBlock->WriteDouble(pGrd->dfMaxY);
711 :
712 : // Z value range
713 16 : poHeaderBlock->WriteFloat(pGrd->fZMin);
714 16 : poHeaderBlock->WriteFloat(pGrd->fZMax);
715 16 : poHeaderBlock->WriteFloat(pGrd->fZMinScale);
716 16 : poHeaderBlock->WriteFloat(pGrd->fZMaxScale);
717 :
718 : // Description String
719 16 : int nChar = static_cast<int>(strlen(pGrd->cDescription));
720 16 : poHeaderBlock->WriteBytes(
721 16 : nChar, reinterpret_cast<const GByte *>(pGrd->cDescription));
722 16 : poHeaderBlock->WriteZeros(32 - nChar);
723 :
724 : // Unit Name String
725 16 : nChar = static_cast<int>(strlen(pGrd->cZUnits));
726 16 : poHeaderBlock->WriteBytes(nChar,
727 16 : reinterpret_cast<const GByte *>(pGrd->cZUnits));
728 16 : poHeaderBlock->WriteZeros(32 - nChar);
729 :
730 : // Ignore 126 - 141 as unknown usage
731 16 : poHeaderBlock->WriteZeros(15);
732 :
733 : // Hill shading
734 16 : poHeaderBlock->WriteInt16(pGrd->bHillShadeExists ? 1 : 0);
735 16 : poHeaderBlock->WriteInt16(0);
736 :
737 16 : poHeaderBlock->WriteByte(pGrd->cHillShadeBrightness);
738 16 : poHeaderBlock->WriteByte(pGrd->cHillShadeContrast);
739 :
740 : // Ignore 147 - 257 as unknown usage
741 16 : poHeaderBlock->WriteZeros(110);
742 :
743 : // Write spatial reference
744 16 : poHeaderBlock->WriteBytes(
745 16 : static_cast<int>(strlen(pGrd->cMICoordSys)),
746 16 : reinterpret_cast<const GByte *>(pGrd->cMICoordSys));
747 16 : poHeaderBlock->WriteZeros(256 -
748 16 : static_cast<int>(strlen(pGrd->cMICoordSys)));
749 :
750 : // Unit code
751 16 : poHeaderBlock->WriteByte(static_cast<GByte>(pGrd->iZUnits));
752 :
753 : // Info on shading
754 16 : GByte byDisplayStatus = 0;
755 16 : if (pGrd->bShowHillShade)
756 : {
757 0 : byDisplayStatus |= 1 << 6;
758 : }
759 16 : if (pGrd->bShowGradient)
760 : {
761 0 : byDisplayStatus |= 1 << 7;
762 : }
763 :
764 16 : poHeaderBlock->WriteByte(byDisplayStatus);
765 16 : poHeaderBlock->WriteInt16(0); // Data Type?
766 :
767 : // Colour inflections
768 16 : poHeaderBlock->WriteInt16(pGrd->iNumColorInflections);
769 64 : for (int i = 0; i < pGrd->iNumColorInflections; i++)
770 : {
771 48 : poHeaderBlock->WriteFloat(pGrd->stInflection[i].zVal);
772 48 : poHeaderBlock->WriteByte(pGrd->stInflection[i].r);
773 48 : poHeaderBlock->WriteByte(pGrd->stInflection[i].g);
774 48 : poHeaderBlock->WriteByte(pGrd->stInflection[i].b);
775 : }
776 :
777 : // Fill in unused blanks
778 16 : poHeaderBlock->WriteZeros((966 - poHeaderBlock->GetCurAddress()));
779 :
780 : // Azimuth and Inclination
781 16 : poHeaderBlock->WriteFloat(pGrd->fHillShadeAzimuth);
782 16 : poHeaderBlock->WriteFloat(pGrd->fHillShadeAngle);
783 :
784 : // Write to disk
785 16 : iStatus = poHeaderBlock->CommitToFile();
786 :
787 16 : delete poHeaderBlock;
788 :
789 : // Update the TAB file to catch any changes
790 16 : if (WriteTab() != 0)
791 10 : iStatus = -1;
792 :
793 16 : return iStatus;
794 : }
795 :
796 16 : int NWT_GRDDataset::WriteTab()
797 : {
798 : // Create the filename for the .tab file.
799 32 : const std::string sTabFile(CPLResetExtensionSafe(pGrd->szFileName, "tab"));
800 :
801 16 : VSILFILE *tabfp = VSIFOpenL(sTabFile.c_str(), "wt");
802 16 : if (tabfp == nullptr)
803 : {
804 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create file `%s'",
805 : sTabFile.c_str());
806 0 : return -1;
807 : }
808 :
809 16 : bool bOK = true;
810 16 : bOK &= VSIFPrintfL(tabfp, "!table\n") > 0;
811 16 : bOK &= VSIFPrintfL(tabfp, "!version 500\n") > 0;
812 16 : bOK &= VSIFPrintfL(tabfp, "!charset %s\n", "Neutral") > 0;
813 16 : bOK &= VSIFPrintfL(tabfp, "\n") > 0;
814 :
815 16 : bOK &= VSIFPrintfL(tabfp, "Definition Table\n") > 0;
816 32 : const std::string path(pGrd->szFileName);
817 16 : const std::string basename = path.substr(path.find_last_of("/\\") + 1);
818 16 : bOK &= VSIFPrintfL(tabfp, " File \"%s\"\n", basename.c_str()) > 0;
819 16 : bOK &= VSIFPrintfL(tabfp, " Type \"RASTER\"\n") > 0;
820 :
821 16 : double dMapUnitsPerPixel =
822 16 : (pGrd->dfMaxX - pGrd->dfMinX) / (static_cast<double>(pGrd->nXSide) - 1);
823 16 : double dShift = dMapUnitsPerPixel / 2.0;
824 :
825 32 : bOK &= VSIFPrintfL(tabfp, " (%f,%f) (%d,%d) Label \"Pt 1\",\n",
826 16 : pGrd->dfMinX - dShift, pGrd->dfMaxY + dShift, 0, 0) > 0;
827 32 : bOK &= VSIFPrintfL(tabfp, " (%f,%f) (%d,%d) Label \"Pt 2\",\n",
828 16 : pGrd->dfMaxX - dShift, pGrd->dfMinY + dShift,
829 16 : pGrd->nXSide - 1, pGrd->nYSide - 1) > 0;
830 32 : bOK &= VSIFPrintfL(tabfp, " (%f,%f) (%d,%d) Label \"Pt 3\"\n",
831 16 : pGrd->dfMinX - dShift, pGrd->dfMinY + dShift, 0,
832 16 : pGrd->nYSide - 1) > 0;
833 :
834 16 : bOK &= VSIFPrintfL(tabfp, " CoordSys %s\n", pGrd->cMICoordSys) > 0;
835 16 : bOK &= VSIFPrintfL(tabfp, " Units \"m\"\n") > 0;
836 :
837 : // Raster Styles.
838 :
839 : // Raster is a grid, which is style 6.
840 16 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 6 1\n") > 0;
841 :
842 : // Brightness - style 1
843 16 : if (pGrd->style.iBrightness > 0)
844 : {
845 32 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 1 %d\n",
846 16 : pGrd->style.iBrightness) > 0;
847 : }
848 :
849 : // Contrast - style 2
850 16 : if (pGrd->style.iContrast > 0)
851 : {
852 32 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 2 %d\n",
853 16 : pGrd->style.iContrast) > 0;
854 : }
855 :
856 : // Greyscale - style 3; only need to write if TRUE
857 16 : if (pGrd->style.bGreyscale == TRUE)
858 : {
859 0 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 3 1\n") > 0;
860 : }
861 :
862 : // Flag to render one colour transparent - style 4
863 16 : if (pGrd->style.bTransparent == TRUE)
864 : {
865 0 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 4 1\n") > 0;
866 0 : if (pGrd->style.iTransColour > 0)
867 : {
868 0 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 7 %d\n",
869 0 : pGrd->style.iTransColour) > 0;
870 : }
871 : }
872 :
873 : // Transparency of immage
874 16 : if (pGrd->style.iTranslucency > 0)
875 : {
876 0 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 8 %d\n",
877 0 : pGrd->style.iTranslucency) > 0;
878 : }
879 :
880 16 : bOK &= VSIFPrintfL(tabfp, "begin_metadata\n") > 0;
881 16 : bOK &= VSIFPrintfL(tabfp, "\"\\MapInfo\" = \"\"\n") > 0;
882 16 : bOK &= VSIFPrintfL(tabfp, "\"\\Vm\" = \"\"\n") > 0;
883 16 : bOK &= VSIFPrintfL(tabfp, "\"\\Vm\\Grid\" = \"Numeric\"\n") > 0;
884 16 : bOK &= VSIFPrintfL(tabfp, "\"\\Vm\\GridName\" = \"%s\"\n",
885 16 : basename.c_str()) > 0;
886 16 : bOK &= VSIFPrintfL(tabfp, "\"\\IsReadOnly\" = \"FALSE\"\n") > 0;
887 16 : bOK &= VSIFPrintfL(tabfp, "end_metadata\n") > 0;
888 :
889 16 : if (VSIFCloseL(tabfp) != 0)
890 0 : bOK = false;
891 :
892 16 : return (bOK) ? 0 : -1;
893 : }
894 :
895 : /************************************************************************/
896 : /* Create() */
897 : /************************************************************************/
898 57 : GDALDataset *NWT_GRDDataset::Create(const char *pszFilename, int nXSize,
899 : int nYSize, int nBandsIn,
900 : GDALDataType eType, char **papszParamList)
901 : {
902 57 : if (nBandsIn != 1)
903 : {
904 18 : CPLError(CE_Failure, CPLE_FileIO,
905 : "Only single band datasets are supported for writing");
906 18 : return nullptr;
907 : }
908 39 : if (eType != GDT_Float32)
909 : {
910 25 : CPLError(CE_Failure, CPLE_FileIO,
911 : "Float32 is the only supported data type");
912 25 : return nullptr;
913 : }
914 14 : NWT_GRDDataset *poDS = new NWT_GRDDataset();
915 14 : poDS->eAccess = GA_Update;
916 14 : poDS->pGrd = static_cast<NWT_GRID *>(calloc(1, sizeof(NWT_GRID)));
917 14 : if (!poDS->pGrd)
918 : {
919 0 : delete poDS;
920 0 : return nullptr;
921 : }
922 :
923 : // We currently only support GRD grid types (could potentially support GRC
924 : // in the papszParamList). Also only support GDT_Float32 as the data type.
925 : // GRD format allows for data to be stretched to 32bit or 16bit integers on
926 : // disk, so it would be feasible to support other data types
927 14 : poDS->pGrd->cFormat = 0x00;
928 :
929 : // File version
930 14 : poDS->pGrd->fVersion = 2.0;
931 :
932 : // Dimensions
933 14 : poDS->pGrd->nXSide = nXSize;
934 14 : poDS->pGrd->nYSide = nYSize;
935 14 : poDS->nRasterXSize = nXSize;
936 14 : poDS->nRasterYSize = nYSize;
937 :
938 : // Some default values to get started with. These will
939 : // be altered when SetGeoTransform is called.
940 14 : poDS->pGrd->dfMinX = -2E+307;
941 14 : poDS->pGrd->dfMinY = -2E+307;
942 14 : poDS->pGrd->dfMaxX = 2E+307;
943 14 : poDS->pGrd->dfMaxY = 2E+307;
944 :
945 : float fZMin, fZMax;
946 : // See if the user passed the min/max values
947 14 : if (CSLFetchNameValue(papszParamList, "ZMIN") == nullptr)
948 : {
949 1 : fZMin = static_cast<float>(-2E+37);
950 : }
951 : else
952 : {
953 13 : fZMin = static_cast<float>(
954 13 : CPLAtof(CSLFetchNameValue(papszParamList, "ZMIN")));
955 : }
956 :
957 14 : if (CSLFetchNameValue(papszParamList, "ZMAX") == nullptr)
958 : {
959 1 : fZMax = static_cast<float>(2E+38);
960 : }
961 : else
962 : {
963 13 : fZMax = static_cast<float>(
964 13 : CPLAtof(CSLFetchNameValue(papszParamList, "ZMAX")));
965 : }
966 :
967 14 : poDS->pGrd->fZMin = fZMin;
968 14 : poDS->pGrd->fZMax = fZMax;
969 : // pGrd->dfStepSize = (pGrd->dfMaxX - pGrd->dfMinX) / (pGrd->nXSide - 1);
970 14 : poDS->pGrd->fZMinScale = fZMin;
971 14 : poDS->pGrd->fZMaxScale = fZMax;
972 : // poDS->pGrd->iZUnits
973 14 : memset(poDS->pGrd->cZUnits, 0, 32);
974 14 : memset(poDS->pGrd->cMICoordSys, 0, 256);
975 :
976 : // Some default colour inflections; Basic scale from blue to red
977 14 : poDS->pGrd->iNumColorInflections = 3;
978 :
979 : // Lowest inflection
980 14 : poDS->pGrd->stInflection[0].zVal = poDS->pGrd->fZMin;
981 14 : poDS->pGrd->stInflection[0].r = 0;
982 14 : poDS->pGrd->stInflection[0].g = 0;
983 14 : poDS->pGrd->stInflection[0].b = 255;
984 :
985 : // Mean inflection
986 14 : poDS->pGrd->stInflection[1].zVal =
987 14 : (poDS->pGrd->fZMax - poDS->pGrd->fZMin) / 2;
988 14 : poDS->pGrd->stInflection[1].r = 255;
989 14 : poDS->pGrd->stInflection[1].g = 255;
990 14 : poDS->pGrd->stInflection[1].b = 0;
991 :
992 : // Highest inflection
993 14 : poDS->pGrd->stInflection[2].zVal = poDS->pGrd->fZMax;
994 14 : poDS->pGrd->stInflection[2].r = 255;
995 14 : poDS->pGrd->stInflection[2].g = 0;
996 14 : poDS->pGrd->stInflection[2].b = 0;
997 :
998 14 : poDS->pGrd->bHillShadeExists = FALSE;
999 14 : poDS->pGrd->bShowGradient = FALSE;
1000 14 : poDS->pGrd->bShowHillShade = FALSE;
1001 14 : poDS->pGrd->cHillShadeBrightness = 0;
1002 14 : poDS->pGrd->cHillShadeContrast = 0;
1003 14 : poDS->pGrd->fHillShadeAzimuth = 0;
1004 14 : poDS->pGrd->fHillShadeAngle = 0;
1005 :
1006 : // Set the raster style settings. These aren't used anywhere other than to
1007 : // write the TAB file
1008 14 : if (CSLFetchNameValue(papszParamList, "BRIGHTNESS") == nullptr)
1009 : {
1010 14 : poDS->pGrd->style.iBrightness = 50;
1011 : }
1012 : else
1013 : {
1014 0 : poDS->pGrd->style.iBrightness =
1015 0 : atoi(CSLFetchNameValue(papszParamList, "BRIGHTNESS"));
1016 : }
1017 :
1018 14 : if (CSLFetchNameValue(papszParamList, "CONTRAST") == nullptr)
1019 : {
1020 14 : poDS->pGrd->style.iContrast = 50;
1021 : }
1022 : else
1023 : {
1024 0 : poDS->pGrd->style.iContrast =
1025 0 : atoi(CSLFetchNameValue(papszParamList, "CONTRAST"));
1026 : }
1027 :
1028 14 : if (CSLFetchNameValue(papszParamList, "TRANSCOLOR") == nullptr)
1029 : {
1030 14 : poDS->pGrd->style.iTransColour = 0;
1031 : }
1032 : else
1033 : {
1034 0 : poDS->pGrd->style.iTransColour =
1035 0 : atoi(CSLFetchNameValue(papszParamList, "TRANSCOLOR"));
1036 : }
1037 :
1038 14 : if (CSLFetchNameValue(papszParamList, "TRANSLUCENCY") == nullptr)
1039 : {
1040 14 : poDS->pGrd->style.iTranslucency = 0;
1041 : }
1042 : else
1043 : {
1044 0 : poDS->pGrd->style.iTranslucency =
1045 0 : atoi(CSLFetchNameValue(papszParamList, "TRANSLUCENCY"));
1046 : }
1047 :
1048 14 : poDS->pGrd->style.bGreyscale = FALSE;
1049 14 : poDS->pGrd->style.bGrey = FALSE;
1050 14 : poDS->pGrd->style.bColour = FALSE;
1051 14 : poDS->pGrd->style.bTransparent = FALSE;
1052 :
1053 : // Open the grid file
1054 14 : poDS->fp = VSIFOpenL(pszFilename, "wb");
1055 14 : if (poDS->fp == nullptr)
1056 : {
1057 1 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create GRD file");
1058 1 : delete poDS;
1059 1 : return nullptr;
1060 : }
1061 :
1062 13 : poDS->pGrd->fp = poDS->fp;
1063 13 : strncpy(poDS->pGrd->szFileName, pszFilename,
1064 : sizeof(poDS->pGrd->szFileName) - 1);
1065 13 : poDS->pGrd->szFileName[sizeof(poDS->pGrd->szFileName) - 1] = '\0';
1066 :
1067 : // Seek to the start of the file and enter the default header info
1068 13 : VSIFSeekL(poDS->fp, 0, SEEK_SET);
1069 13 : if (poDS->UpdateHeader() != 0)
1070 : {
1071 10 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create GRD file");
1072 10 : delete poDS;
1073 10 : return nullptr;
1074 : }
1075 :
1076 : /* -------------------------------------------------------------------- */
1077 : /* Create band information objects; */
1078 : /* Only 1 band is allowed */
1079 : /* -------------------------------------------------------------------- */
1080 3 : poDS->SetBand(1, new NWT_GRDRasterBand(poDS, 1, 1)); // z
1081 :
1082 3 : poDS->oOvManager.Initialize(poDS, pszFilename);
1083 3 : poDS->FlushCache(false); // Write the header to disk.
1084 :
1085 3 : return poDS;
1086 : }
1087 :
1088 : /************************************************************************/
1089 : /* CreateCopy() */
1090 : /************************************************************************/
1091 30 : GDALDataset *NWT_GRDDataset::CreateCopy(const char *pszFilename,
1092 : GDALDataset *poSrcDS, int bStrict,
1093 : char **papszOptions,
1094 : GDALProgressFunc pfnProgress,
1095 : void *pProgressData)
1096 : {
1097 :
1098 30 : if (poSrcDS->GetRasterCount() != 1)
1099 : {
1100 5 : CPLError(CE_Failure, CPLE_FileIO,
1101 : "Only single band datasets are supported for writing");
1102 5 : return nullptr;
1103 : }
1104 :
1105 25 : char **tmpOptions = CSLDuplicate(papszOptions);
1106 :
1107 : /*
1108 : * Compute the statistics if ZMAX and ZMIN are not provided
1109 : */
1110 25 : double dfMin = 0.0;
1111 25 : double dfMax = 0.0;
1112 25 : double dfMean = 0.0;
1113 25 : double dfStdDev = 0.0;
1114 25 : GDALRasterBand *pBand = poSrcDS->GetRasterBand(1);
1115 25 : char sMax[10] = {};
1116 25 : char sMin[10] = {};
1117 :
1118 25 : if ((CSLFetchNameValue(papszOptions, "ZMAX") == nullptr) ||
1119 0 : (CSLFetchNameValue(papszOptions, "ZMIN") == nullptr))
1120 : {
1121 25 : CPL_IGNORE_RET_VAL(pBand->GetStatistics(FALSE, TRUE, &dfMin, &dfMax,
1122 25 : &dfMean, &dfStdDev));
1123 : }
1124 :
1125 25 : if (CSLFetchNameValue(papszOptions, "ZMAX") == nullptr)
1126 : {
1127 25 : CPLsnprintf(sMax, sizeof(sMax), "%f", dfMax);
1128 25 : tmpOptions = CSLSetNameValue(tmpOptions, "ZMAX", sMax);
1129 : }
1130 25 : if (CSLFetchNameValue(papszOptions, "ZMIN") == nullptr)
1131 : {
1132 25 : CPLsnprintf(sMin, sizeof(sMin), "%f", dfMin);
1133 25 : tmpOptions = CSLSetNameValue(tmpOptions, "ZMIN", sMin);
1134 : }
1135 :
1136 : GDALDriver *poDriver =
1137 25 : GDALDriver::FromHandle(GDALGetDriverByName("NWT_GRD"));
1138 25 : GDALDataset *poDstDS = poDriver->DefaultCreateCopy(
1139 : pszFilename, poSrcDS, bStrict, tmpOptions, pfnProgress, pProgressData);
1140 :
1141 25 : CSLDestroy(tmpOptions);
1142 :
1143 25 : return poDstDS;
1144 : }
1145 : #endif // NO_MITAB_SUPPORT
1146 :
1147 : /************************************************************************/
1148 : /* GDALRegister_GRD() */
1149 : /************************************************************************/
1150 2038 : void GDALRegister_NWT_GRD()
1151 : {
1152 2038 : if (GDALGetDriverByName("NWT_GRD") != nullptr)
1153 283 : return;
1154 :
1155 1755 : GDALDriver *poDriver = new GDALDriver();
1156 :
1157 1755 : poDriver->SetDescription("NWT_GRD");
1158 1755 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1159 1755 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1160 1755 : "Northwood Numeric Grid Format .grd/.tab");
1161 1755 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/nwtgrd.html");
1162 1755 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd");
1163 1755 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1164 :
1165 1755 : poDriver->SetMetadataItem(
1166 : GDAL_DMD_OPENOPTIONLIST,
1167 : "<OpenOptionList>"
1168 : " <Option name='BAND_COUNT' type='int' description='1 (Z) or 4 "
1169 : "(RGBZ). Only used in read-only mode' default='4'/>"
1170 1755 : "</OpenOptionList>");
1171 :
1172 1755 : poDriver->pfnOpen = NWT_GRDDataset::Open;
1173 1755 : poDriver->pfnIdentify = NWT_GRDDataset::Identify;
1174 :
1175 : #ifndef NO_MITAB_SUPPORT
1176 :
1177 1755 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Float32");
1178 1755 : poDriver->SetMetadataItem(
1179 : GDAL_DMD_CREATIONOPTIONLIST,
1180 : "<CreationOptionList>"
1181 : " <Option name='ZMIN' type='float' description='Minimum cell value "
1182 : "of raster for defining RGB scaling' default='-2E+37'/>"
1183 : " <Option name='ZMAX' type='float' description='Maximum cell value "
1184 : "of raster for defining RGB scaling' default='2E+38'/>"
1185 : " <Option name='BRIGHTNESS' type='int' description='Brightness to "
1186 : "be recorded in TAB file. Only affects reading with MapInfo' "
1187 : "default='50'/>"
1188 : " <Option name='CONTRAST' type='int' description='Contrast to be "
1189 : "recorded in TAB file. Only affects reading with MapInfo' "
1190 : "default='50'/>"
1191 : " <Option name='TRANSCOLOR' type='int' description='Transparent "
1192 : "color to be recorded in TAB file. Only affects reading with MapInfo' "
1193 : "default='0'/>"
1194 : " <Option name='TRANSLUCENCY' type='int' description='Level of "
1195 : "translucency to be recorded in TAB file. Only affects reading with "
1196 : "MapInfo' default='0'/>"
1197 1755 : "</CreationOptionList>");
1198 :
1199 1755 : poDriver->pfnCreate = NWT_GRDDataset::Create;
1200 1755 : poDriver->pfnCreateCopy = NWT_GRDDataset::CreateCopy;
1201 : #endif
1202 :
1203 1755 : GetGDALDriverManager()->RegisterDriver(poDriver);
1204 : }
|