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