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 : if (pGrd)
450 : {
451 26 : pGrd->fp = nullptr; // this prevents nwtCloseGrid from closing the fp
452 26 : nwtCloseGrid(pGrd);
453 : }
454 26 : if (m_poSRS)
455 1 : m_poSRS->Release();
456 :
457 26 : if (fp != nullptr)
458 25 : VSIFCloseL(fp);
459 52 : }
460 :
461 : /************************************************************************/
462 : /* ~FlushCache(bool bAtClosing) */
463 : /************************************************************************/
464 17 : CPLErr NWT_GRDDataset::FlushCache(bool bAtClosing)
465 : {
466 : // Ensure the header and TAB file are up to date
467 17 : if (bUpdateHeader && pGrd)
468 : {
469 : #ifndef NO_MITAB_SUPPORT
470 3 : UpdateHeader();
471 : #endif
472 : }
473 :
474 : // Call the parent method
475 17 : return GDALPamDataset::FlushCache(bAtClosing);
476 : }
477 :
478 : /************************************************************************/
479 : /* GetGeoTransform() */
480 : /************************************************************************/
481 :
482 3 : CPLErr NWT_GRDDataset::GetGeoTransform(double *padfTransform)
483 : {
484 3 : padfTransform[0] = pGrd->dfMinX - (pGrd->dfStepSize * 0.5);
485 3 : padfTransform[3] = pGrd->dfMaxY + (pGrd->dfStepSize * 0.5);
486 3 : padfTransform[1] = pGrd->dfStepSize;
487 3 : padfTransform[2] = 0.0;
488 :
489 3 : padfTransform[4] = 0.0;
490 3 : padfTransform[5] = -1 * pGrd->dfStepSize;
491 :
492 3 : return CE_None;
493 : }
494 :
495 : /************************************************************************/
496 : /* SetGeoTransform() */
497 : /************************************************************************/
498 :
499 3 : CPLErr NWT_GRDDataset::SetGeoTransform(double *padfTransform)
500 : {
501 3 : if (padfTransform[2] != 0.0 || padfTransform[4] != 0.0)
502 : {
503 :
504 0 : CPLError(CE_Failure, CPLE_NotSupported,
505 : "GRD datasets do not support skew/rotation");
506 0 : return CE_Failure;
507 : }
508 3 : pGrd->dfStepSize = padfTransform[1];
509 :
510 : // GRD format sets the min/max coordinates to the centre of the
511 : // cell; We must account for this when copying the GDAL geotransform
512 : // which references the top left corner
513 3 : pGrd->dfMinX = padfTransform[0] + (pGrd->dfStepSize * 0.5);
514 3 : pGrd->dfMaxY = padfTransform[3] - (pGrd->dfStepSize * 0.5);
515 :
516 : // Now set the miny and maxx
517 3 : pGrd->dfMaxX = pGrd->dfMinX + (pGrd->dfStepSize * (nRasterXSize - 1));
518 3 : pGrd->dfMinY = pGrd->dfMaxY - (pGrd->dfStepSize * (nRasterYSize - 1));
519 3 : bUpdateHeader = true;
520 :
521 3 : return CE_None;
522 : }
523 :
524 : /************************************************************************/
525 : /* GetSpatialRef() */
526 : /************************************************************************/
527 1 : const OGRSpatialReference *NWT_GRDDataset::GetSpatialRef() const
528 : {
529 :
530 : // First try getting it from the PAM dataset
531 1 : const OGRSpatialReference *poSRS = GDALPamDataset::GetSpatialRef();
532 1 : if (poSRS)
533 0 : return poSRS;
534 :
535 1 : if (m_poSRS)
536 0 : return m_poSRS;
537 :
538 : // If that isn't possible, read it from the GRD file. This may be a less
539 : // complete projection string.
540 : OGRSpatialReference *poSpatialRef =
541 1 : MITABCoordSys2SpatialRef(pGrd->cMICoordSys);
542 1 : m_poSRS = poSpatialRef;
543 :
544 1 : return m_poSRS;
545 : }
546 :
547 : #ifndef NO_MITAB_SUPPORT
548 : /************************************************************************/
549 : /* SetSpatialRef() */
550 : /************************************************************************/
551 :
552 3 : CPLErr NWT_GRDDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
553 : {
554 :
555 3 : char *psTABProj = MITABSpatialRef2CoordSys(poSRS);
556 3 : strncpy(pGrd->cMICoordSys, psTABProj, sizeof(pGrd->cMICoordSys) - 1);
557 3 : pGrd->cMICoordSys[255] = '\0';
558 :
559 : // Free temp projection.
560 3 : CPLFree(psTABProj);
561 : // Set projection in PAM dataset, so that
562 : // GDAL can always retrieve the complete projection.
563 3 : GDALPamDataset::SetSpatialRef(poSRS);
564 3 : bUpdateHeader = true;
565 :
566 3 : return CE_None;
567 : }
568 : #endif
569 :
570 : /************************************************************************/
571 : /* Identify() */
572 : /************************************************************************/
573 :
574 51460 : int NWT_GRDDataset::Identify(GDALOpenInfo *poOpenInfo)
575 : {
576 : /* -------------------------------------------------------------------- */
577 : /* Look for the header */
578 : /* -------------------------------------------------------------------- */
579 51460 : if (poOpenInfo->nHeaderBytes < 1024)
580 49650 : return FALSE;
581 :
582 1810 : if (poOpenInfo->pabyHeader[0] != 'H' || poOpenInfo->pabyHeader[1] != 'G' ||
583 27 : poOpenInfo->pabyHeader[2] != 'P' || poOpenInfo->pabyHeader[3] != 'C' ||
584 27 : poOpenInfo->pabyHeader[4] != '1')
585 1786 : return FALSE;
586 :
587 24 : return TRUE;
588 : }
589 :
590 : /************************************************************************/
591 : /* Open() */
592 : /************************************************************************/
593 12 : GDALDataset *NWT_GRDDataset::Open(GDALOpenInfo *poOpenInfo)
594 : {
595 12 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
596 0 : return nullptr;
597 :
598 : /* -------------------------------------------------------------------- */
599 : /* Create a corresponding GDALDataset. */
600 : /* -------------------------------------------------------------------- */
601 12 : int nBandsToCreate = 0;
602 :
603 12 : NWT_GRDDataset *poDS = new NWT_GRDDataset();
604 :
605 12 : poDS->fp = poOpenInfo->fpL;
606 12 : poOpenInfo->fpL = nullptr;
607 :
608 12 : if (poOpenInfo->eAccess == GA_Update)
609 : {
610 0 : nBandsToCreate = 1;
611 : }
612 : else
613 : {
614 12 : nBandsToCreate = atoi(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
615 : "BAND_COUNT", "4"));
616 12 : if (nBandsToCreate != 1 && nBandsToCreate != 4)
617 : {
618 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for BAND_COUNT");
619 0 : delete poDS;
620 0 : return nullptr;
621 : }
622 : }
623 12 : poDS->eAccess = poOpenInfo->eAccess;
624 :
625 : /* -------------------------------------------------------------------- */
626 : /* Read the header. */
627 : /* -------------------------------------------------------------------- */
628 12 : VSIFSeekL(poDS->fp, 0, SEEK_SET);
629 12 : VSIFReadL(poDS->abyHeader, 1, 1024, poDS->fp);
630 12 : poDS->pGrd = reinterpret_cast<NWT_GRID *>(calloc(1, sizeof(NWT_GRID)));
631 12 : if (!poDS->pGrd)
632 : {
633 0 : delete poDS;
634 0 : return nullptr;
635 : }
636 :
637 12 : poDS->pGrd->fp = poDS->fp;
638 :
639 24 : if (!nwt_ParseHeader(poDS->pGrd, poDS->abyHeader) ||
640 12 : !GDALCheckDatasetDimensions(poDS->pGrd->nXSide, poDS->pGrd->nYSide))
641 : {
642 0 : delete poDS;
643 0 : return nullptr;
644 : }
645 :
646 12 : poDS->nRasterXSize = poDS->pGrd->nXSide;
647 12 : poDS->nRasterYSize = poDS->pGrd->nYSide;
648 :
649 : // create a colorTable
650 : // if( poDS->pGrd->iNumColorInflections > 0 )
651 : // poDS->CreateColorTable();
652 12 : nwt_LoadColors(poDS->ColorMap, 4096, poDS->pGrd);
653 : /* -------------------------------------------------------------------- */
654 : /* Create band information objects. */
655 : /* If opening in read-only mode, then we create 4 bands (RGBZ) */
656 : /* with data values being available in band 4. If opening in update mode*/
657 : /* we create 1 band (the data values). This is because in reality, there*/
658 : /* is only 1 band stored on disk. The RGB bands are 'virtual' - derived */
659 : /* from the data values on the fly */
660 : /* -------------------------------------------------------------------- */
661 54 : for (int i = 0; i < nBandsToCreate; ++i)
662 : {
663 42 : poDS->SetBand(i + 1,
664 42 : new NWT_GRDRasterBand(poDS, i + 1, nBandsToCreate));
665 : }
666 :
667 : /* -------------------------------------------------------------------- */
668 : /* Initialize any PAM information. */
669 : /* -------------------------------------------------------------------- */
670 12 : poDS->SetDescription(poOpenInfo->pszFilename);
671 12 : poDS->TryLoadXML();
672 :
673 : /* -------------------------------------------------------------------- */
674 : /* Check for external overviews. */
675 : /* -------------------------------------------------------------------- */
676 24 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
677 12 : poOpenInfo->GetSiblingFiles());
678 :
679 12 : return poDS;
680 : }
681 :
682 : #ifndef NO_MITAB_SUPPORT
683 : /************************************************************************/
684 : /* UpdateHeader() */
685 : /************************************************************************/
686 16 : int NWT_GRDDataset::UpdateHeader()
687 : {
688 16 : int iStatus = 0;
689 16 : TABRawBinBlock *poHeaderBlock = new TABRawBinBlock(TABReadWrite, TRUE);
690 16 : poHeaderBlock->InitNewBlock(fp, 1024);
691 :
692 : // Write the header string
693 16 : poHeaderBlock->WriteBytes(5, reinterpret_cast<const GByte *>("HGPC1\0"));
694 :
695 : // Version number
696 16 : poHeaderBlock->WriteFloat(pGrd->fVersion);
697 :
698 : // Dimensions
699 16 : poHeaderBlock->WriteInt16(static_cast<GInt16>(pGrd->nXSide));
700 16 : poHeaderBlock->WriteInt16(static_cast<GInt16>(pGrd->nYSide));
701 :
702 : // Extents
703 16 : poHeaderBlock->WriteDouble(pGrd->dfMinX);
704 16 : poHeaderBlock->WriteDouble(pGrd->dfMaxX);
705 16 : poHeaderBlock->WriteDouble(pGrd->dfMinY);
706 16 : poHeaderBlock->WriteDouble(pGrd->dfMaxY);
707 :
708 : // Z value range
709 16 : poHeaderBlock->WriteFloat(pGrd->fZMin);
710 16 : poHeaderBlock->WriteFloat(pGrd->fZMax);
711 16 : poHeaderBlock->WriteFloat(pGrd->fZMinScale);
712 16 : poHeaderBlock->WriteFloat(pGrd->fZMaxScale);
713 :
714 : // Description String
715 16 : int nChar = static_cast<int>(strlen(pGrd->cDescription));
716 16 : poHeaderBlock->WriteBytes(
717 16 : nChar, reinterpret_cast<const GByte *>(pGrd->cDescription));
718 16 : poHeaderBlock->WriteZeros(32 - nChar);
719 :
720 : // Unit Name String
721 16 : nChar = static_cast<int>(strlen(pGrd->cZUnits));
722 16 : poHeaderBlock->WriteBytes(nChar,
723 16 : reinterpret_cast<const GByte *>(pGrd->cZUnits));
724 16 : poHeaderBlock->WriteZeros(32 - nChar);
725 :
726 : // Ignore 126 - 141 as unknown usage
727 16 : poHeaderBlock->WriteZeros(15);
728 :
729 : // Hill shading
730 16 : poHeaderBlock->WriteInt16(pGrd->bHillShadeExists ? 1 : 0);
731 16 : poHeaderBlock->WriteInt16(0);
732 :
733 16 : poHeaderBlock->WriteByte(pGrd->cHillShadeBrightness);
734 16 : poHeaderBlock->WriteByte(pGrd->cHillShadeContrast);
735 :
736 : // Ignore 147 - 257 as unknown usage
737 16 : poHeaderBlock->WriteZeros(110);
738 :
739 : // Write spatial reference
740 16 : poHeaderBlock->WriteBytes(
741 16 : static_cast<int>(strlen(pGrd->cMICoordSys)),
742 16 : reinterpret_cast<const GByte *>(pGrd->cMICoordSys));
743 16 : poHeaderBlock->WriteZeros(256 -
744 16 : static_cast<int>(strlen(pGrd->cMICoordSys)));
745 :
746 : // Unit code
747 16 : poHeaderBlock->WriteByte(static_cast<GByte>(pGrd->iZUnits));
748 :
749 : // Info on shading
750 16 : GByte byDisplayStatus = 0;
751 16 : if (pGrd->bShowHillShade)
752 : {
753 0 : byDisplayStatus |= 1 << 6;
754 : }
755 16 : if (pGrd->bShowGradient)
756 : {
757 0 : byDisplayStatus |= 1 << 7;
758 : }
759 :
760 16 : poHeaderBlock->WriteByte(byDisplayStatus);
761 16 : poHeaderBlock->WriteInt16(0); // Data Type?
762 :
763 : // Colour inflections
764 16 : poHeaderBlock->WriteInt16(pGrd->iNumColorInflections);
765 64 : for (int i = 0; i < pGrd->iNumColorInflections; i++)
766 : {
767 48 : poHeaderBlock->WriteFloat(pGrd->stInflection[i].zVal);
768 48 : poHeaderBlock->WriteByte(pGrd->stInflection[i].r);
769 48 : poHeaderBlock->WriteByte(pGrd->stInflection[i].g);
770 48 : poHeaderBlock->WriteByte(pGrd->stInflection[i].b);
771 : }
772 :
773 : // Fill in unused blanks
774 16 : poHeaderBlock->WriteZeros((966 - poHeaderBlock->GetCurAddress()));
775 :
776 : // Azimuth and Inclination
777 16 : poHeaderBlock->WriteFloat(pGrd->fHillShadeAzimuth);
778 16 : poHeaderBlock->WriteFloat(pGrd->fHillShadeAngle);
779 :
780 : // Write to disk
781 16 : iStatus = poHeaderBlock->CommitToFile();
782 :
783 16 : delete poHeaderBlock;
784 :
785 : // Update the TAB file to catch any changes
786 16 : if (WriteTab() != 0)
787 10 : iStatus = -1;
788 :
789 16 : return iStatus;
790 : }
791 :
792 16 : int NWT_GRDDataset::WriteTab()
793 : {
794 : // Create the filename for the .tab file.
795 32 : const std::string sTabFile(CPLResetExtensionSafe(pGrd->szFileName, "tab"));
796 :
797 16 : VSILFILE *tabfp = VSIFOpenL(sTabFile.c_str(), "wt");
798 16 : if (tabfp == nullptr)
799 : {
800 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create file `%s'",
801 : sTabFile.c_str());
802 0 : return -1;
803 : }
804 :
805 16 : bool bOK = true;
806 16 : bOK &= VSIFPrintfL(tabfp, "!table\n") > 0;
807 16 : bOK &= VSIFPrintfL(tabfp, "!version 500\n") > 0;
808 16 : bOK &= VSIFPrintfL(tabfp, "!charset %s\n", "Neutral") > 0;
809 16 : bOK &= VSIFPrintfL(tabfp, "\n") > 0;
810 :
811 16 : bOK &= VSIFPrintfL(tabfp, "Definition Table\n") > 0;
812 32 : const std::string path(pGrd->szFileName);
813 16 : const std::string basename = path.substr(path.find_last_of("/\\") + 1);
814 16 : bOK &= VSIFPrintfL(tabfp, " File \"%s\"\n", basename.c_str()) > 0;
815 16 : bOK &= VSIFPrintfL(tabfp, " Type \"RASTER\"\n") > 0;
816 :
817 16 : double dMapUnitsPerPixel =
818 16 : (pGrd->dfMaxX - pGrd->dfMinX) / (static_cast<double>(pGrd->nXSide) - 1);
819 16 : double dShift = dMapUnitsPerPixel / 2.0;
820 :
821 32 : bOK &= VSIFPrintfL(tabfp, " (%f,%f) (%d,%d) Label \"Pt 1\",\n",
822 16 : pGrd->dfMinX - dShift, pGrd->dfMaxY + dShift, 0, 0) > 0;
823 32 : bOK &= VSIFPrintfL(tabfp, " (%f,%f) (%d,%d) Label \"Pt 2\",\n",
824 16 : pGrd->dfMaxX - dShift, pGrd->dfMinY + dShift,
825 16 : pGrd->nXSide - 1, pGrd->nYSide - 1) > 0;
826 32 : bOK &= VSIFPrintfL(tabfp, " (%f,%f) (%d,%d) Label \"Pt 3\"\n",
827 16 : pGrd->dfMinX - dShift, pGrd->dfMinY + dShift, 0,
828 16 : pGrd->nYSide - 1) > 0;
829 :
830 16 : bOK &= VSIFPrintfL(tabfp, " CoordSys %s\n", pGrd->cMICoordSys) > 0;
831 16 : bOK &= VSIFPrintfL(tabfp, " Units \"m\"\n") > 0;
832 :
833 : // Raster Styles.
834 :
835 : // Raster is a grid, which is style 6.
836 16 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 6 1\n") > 0;
837 :
838 : // Brightness - style 1
839 16 : if (pGrd->style.iBrightness > 0)
840 : {
841 32 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 1 %d\n",
842 16 : pGrd->style.iBrightness) > 0;
843 : }
844 :
845 : // Contrast - style 2
846 16 : if (pGrd->style.iContrast > 0)
847 : {
848 32 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 2 %d\n",
849 16 : pGrd->style.iContrast) > 0;
850 : }
851 :
852 : // Greyscale - style 3; only need to write if TRUE
853 16 : if (pGrd->style.bGreyscale == TRUE)
854 : {
855 0 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 3 1\n") > 0;
856 : }
857 :
858 : // Flag to render one colour transparent - style 4
859 16 : if (pGrd->style.bTransparent == TRUE)
860 : {
861 0 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 4 1\n") > 0;
862 0 : if (pGrd->style.iTransColour > 0)
863 : {
864 0 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 7 %d\n",
865 0 : pGrd->style.iTransColour) > 0;
866 : }
867 : }
868 :
869 : // Transparency of immage
870 16 : if (pGrd->style.iTranslucency > 0)
871 : {
872 0 : bOK &= VSIFPrintfL(tabfp, " RasterStyle 8 %d\n",
873 0 : pGrd->style.iTranslucency) > 0;
874 : }
875 :
876 16 : bOK &= VSIFPrintfL(tabfp, "begin_metadata\n") > 0;
877 16 : bOK &= VSIFPrintfL(tabfp, "\"\\MapInfo\" = \"\"\n") > 0;
878 16 : bOK &= VSIFPrintfL(tabfp, "\"\\Vm\" = \"\"\n") > 0;
879 16 : bOK &= VSIFPrintfL(tabfp, "\"\\Vm\\Grid\" = \"Numeric\"\n") > 0;
880 16 : bOK &= VSIFPrintfL(tabfp, "\"\\Vm\\GridName\" = \"%s\"\n",
881 16 : basename.c_str()) > 0;
882 16 : bOK &= VSIFPrintfL(tabfp, "\"\\IsReadOnly\" = \"FALSE\"\n") > 0;
883 16 : bOK &= VSIFPrintfL(tabfp, "end_metadata\n") > 0;
884 :
885 16 : if (VSIFCloseL(tabfp) != 0)
886 0 : bOK = false;
887 :
888 16 : return (bOK) ? 0 : -1;
889 : }
890 :
891 : /************************************************************************/
892 : /* Create() */
893 : /************************************************************************/
894 57 : GDALDataset *NWT_GRDDataset::Create(const char *pszFilename, int nXSize,
895 : int nYSize, int nBandsIn,
896 : GDALDataType eType, char **papszParamList)
897 : {
898 57 : if (nBandsIn != 1)
899 : {
900 18 : CPLError(CE_Failure, CPLE_FileIO,
901 : "Only single band datasets are supported for writing");
902 18 : return nullptr;
903 : }
904 39 : if (eType != GDT_Float32)
905 : {
906 25 : CPLError(CE_Failure, CPLE_FileIO,
907 : "Float32 is the only supported data type");
908 25 : return nullptr;
909 : }
910 14 : NWT_GRDDataset *poDS = new NWT_GRDDataset();
911 14 : poDS->eAccess = GA_Update;
912 14 : poDS->pGrd = static_cast<NWT_GRID *>(calloc(1, sizeof(NWT_GRID)));
913 14 : if (!poDS->pGrd)
914 : {
915 0 : delete poDS;
916 0 : return nullptr;
917 : }
918 :
919 : // We currently only support GRD grid types (could potentially support GRC
920 : // in the papszParamList). Also only support GDT_Float32 as the data type.
921 : // GRD format allows for data to be stretched to 32bit or 16bit integers on
922 : // disk, so it would be feasible to support other data types
923 14 : poDS->pGrd->cFormat = 0x00;
924 :
925 : // File version
926 14 : poDS->pGrd->fVersion = 2.0;
927 :
928 : // Dimensions
929 14 : poDS->pGrd->nXSide = nXSize;
930 14 : poDS->pGrd->nYSide = nYSize;
931 14 : poDS->nRasterXSize = nXSize;
932 14 : poDS->nRasterYSize = nYSize;
933 :
934 : // Some default values to get started with. These will
935 : // be altered when SetGeoTransform is called.
936 14 : poDS->pGrd->dfMinX = -2E+307;
937 14 : poDS->pGrd->dfMinY = -2E+307;
938 14 : poDS->pGrd->dfMaxX = 2E+307;
939 14 : poDS->pGrd->dfMaxY = 2E+307;
940 :
941 : float fZMin, fZMax;
942 : // See if the user passed the min/max values
943 14 : if (CSLFetchNameValue(papszParamList, "ZMIN") == nullptr)
944 : {
945 1 : fZMin = static_cast<float>(-2E+37);
946 : }
947 : else
948 : {
949 13 : fZMin = static_cast<float>(
950 13 : CPLAtof(CSLFetchNameValue(papszParamList, "ZMIN")));
951 : }
952 :
953 14 : if (CSLFetchNameValue(papszParamList, "ZMAX") == nullptr)
954 : {
955 1 : fZMax = static_cast<float>(2E+38);
956 : }
957 : else
958 : {
959 13 : fZMax = static_cast<float>(
960 13 : CPLAtof(CSLFetchNameValue(papszParamList, "ZMAX")));
961 : }
962 :
963 14 : poDS->pGrd->fZMin = fZMin;
964 14 : poDS->pGrd->fZMax = fZMax;
965 : // pGrd->dfStepSize = (pGrd->dfMaxX - pGrd->dfMinX) / (pGrd->nXSide - 1);
966 14 : poDS->pGrd->fZMinScale = fZMin;
967 14 : poDS->pGrd->fZMaxScale = fZMax;
968 : // poDS->pGrd->iZUnits
969 14 : memset(poDS->pGrd->cZUnits, 0, 32);
970 14 : memset(poDS->pGrd->cMICoordSys, 0, 256);
971 :
972 : // Some default colour inflections; Basic scale from blue to red
973 14 : poDS->pGrd->iNumColorInflections = 3;
974 :
975 : // Lowest inflection
976 14 : poDS->pGrd->stInflection[0].zVal = poDS->pGrd->fZMin;
977 14 : poDS->pGrd->stInflection[0].r = 0;
978 14 : poDS->pGrd->stInflection[0].g = 0;
979 14 : poDS->pGrd->stInflection[0].b = 255;
980 :
981 : // Mean inflection
982 14 : poDS->pGrd->stInflection[1].zVal =
983 14 : (poDS->pGrd->fZMax - poDS->pGrd->fZMin) / 2;
984 14 : poDS->pGrd->stInflection[1].r = 255;
985 14 : poDS->pGrd->stInflection[1].g = 255;
986 14 : poDS->pGrd->stInflection[1].b = 0;
987 :
988 : // Highest inflection
989 14 : poDS->pGrd->stInflection[2].zVal = poDS->pGrd->fZMax;
990 14 : poDS->pGrd->stInflection[2].r = 255;
991 14 : poDS->pGrd->stInflection[2].g = 0;
992 14 : poDS->pGrd->stInflection[2].b = 0;
993 :
994 14 : poDS->pGrd->bHillShadeExists = FALSE;
995 14 : poDS->pGrd->bShowGradient = FALSE;
996 14 : poDS->pGrd->bShowHillShade = FALSE;
997 14 : poDS->pGrd->cHillShadeBrightness = 0;
998 14 : poDS->pGrd->cHillShadeContrast = 0;
999 14 : poDS->pGrd->fHillShadeAzimuth = 0;
1000 14 : poDS->pGrd->fHillShadeAngle = 0;
1001 :
1002 : // Set the raster style settings. These aren't used anywhere other than to
1003 : // write the TAB file
1004 14 : if (CSLFetchNameValue(papszParamList, "BRIGHTNESS") == nullptr)
1005 : {
1006 14 : poDS->pGrd->style.iBrightness = 50;
1007 : }
1008 : else
1009 : {
1010 0 : poDS->pGrd->style.iBrightness =
1011 0 : atoi(CSLFetchNameValue(papszParamList, "BRIGHTNESS"));
1012 : }
1013 :
1014 14 : if (CSLFetchNameValue(papszParamList, "CONTRAST") == nullptr)
1015 : {
1016 14 : poDS->pGrd->style.iContrast = 50;
1017 : }
1018 : else
1019 : {
1020 0 : poDS->pGrd->style.iContrast =
1021 0 : atoi(CSLFetchNameValue(papszParamList, "CONTRAST"));
1022 : }
1023 :
1024 14 : if (CSLFetchNameValue(papszParamList, "TRANSCOLOR") == nullptr)
1025 : {
1026 14 : poDS->pGrd->style.iTransColour = 0;
1027 : }
1028 : else
1029 : {
1030 0 : poDS->pGrd->style.iTransColour =
1031 0 : atoi(CSLFetchNameValue(papszParamList, "TRANSCOLOR"));
1032 : }
1033 :
1034 14 : if (CSLFetchNameValue(papszParamList, "TRANSLUCENCY") == nullptr)
1035 : {
1036 14 : poDS->pGrd->style.iTranslucency = 0;
1037 : }
1038 : else
1039 : {
1040 0 : poDS->pGrd->style.iTranslucency =
1041 0 : atoi(CSLFetchNameValue(papszParamList, "TRANSLUCENCY"));
1042 : }
1043 :
1044 14 : poDS->pGrd->style.bGreyscale = FALSE;
1045 14 : poDS->pGrd->style.bGrey = FALSE;
1046 14 : poDS->pGrd->style.bColour = FALSE;
1047 14 : poDS->pGrd->style.bTransparent = FALSE;
1048 :
1049 : // Open the grid file
1050 14 : poDS->fp = VSIFOpenL(pszFilename, "wb");
1051 14 : if (poDS->fp == nullptr)
1052 : {
1053 1 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create GRD file");
1054 1 : delete poDS;
1055 1 : return nullptr;
1056 : }
1057 :
1058 13 : poDS->pGrd->fp = poDS->fp;
1059 13 : strncpy(poDS->pGrd->szFileName, pszFilename,
1060 : sizeof(poDS->pGrd->szFileName) - 1);
1061 13 : poDS->pGrd->szFileName[sizeof(poDS->pGrd->szFileName) - 1] = '\0';
1062 :
1063 : // Seek to the start of the file and enter the default header info
1064 13 : VSIFSeekL(poDS->fp, 0, SEEK_SET);
1065 13 : if (poDS->UpdateHeader() != 0)
1066 : {
1067 10 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create GRD file");
1068 10 : delete poDS;
1069 10 : return nullptr;
1070 : }
1071 :
1072 : /* -------------------------------------------------------------------- */
1073 : /* Create band information objects; */
1074 : /* Only 1 band is allowed */
1075 : /* -------------------------------------------------------------------- */
1076 3 : poDS->SetBand(1, new NWT_GRDRasterBand(poDS, 1, 1)); // z
1077 :
1078 3 : poDS->oOvManager.Initialize(poDS, pszFilename);
1079 3 : poDS->FlushCache(false); // Write the header to disk.
1080 :
1081 3 : return poDS;
1082 : }
1083 :
1084 : /************************************************************************/
1085 : /* CreateCopy() */
1086 : /************************************************************************/
1087 30 : GDALDataset *NWT_GRDDataset::CreateCopy(const char *pszFilename,
1088 : GDALDataset *poSrcDS, int bStrict,
1089 : char **papszOptions,
1090 : GDALProgressFunc pfnProgress,
1091 : void *pProgressData)
1092 : {
1093 :
1094 30 : if (poSrcDS->GetRasterCount() != 1)
1095 : {
1096 5 : CPLError(CE_Failure, CPLE_FileIO,
1097 : "Only single band datasets are supported for writing");
1098 5 : return nullptr;
1099 : }
1100 :
1101 25 : char **tmpOptions = CSLDuplicate(papszOptions);
1102 :
1103 : /*
1104 : * Compute the statistics if ZMAX and ZMIN are not provided
1105 : */
1106 25 : double dfMin = 0.0;
1107 25 : double dfMax = 0.0;
1108 25 : double dfMean = 0.0;
1109 25 : double dfStdDev = 0.0;
1110 25 : GDALRasterBand *pBand = poSrcDS->GetRasterBand(1);
1111 25 : char sMax[10] = {};
1112 25 : char sMin[10] = {};
1113 :
1114 25 : if ((CSLFetchNameValue(papszOptions, "ZMAX") == nullptr) ||
1115 0 : (CSLFetchNameValue(papszOptions, "ZMIN") == nullptr))
1116 : {
1117 25 : CPL_IGNORE_RET_VAL(pBand->GetStatistics(FALSE, TRUE, &dfMin, &dfMax,
1118 25 : &dfMean, &dfStdDev));
1119 : }
1120 :
1121 25 : if (CSLFetchNameValue(papszOptions, "ZMAX") == nullptr)
1122 : {
1123 25 : CPLsnprintf(sMax, sizeof(sMax), "%f", dfMax);
1124 25 : tmpOptions = CSLSetNameValue(tmpOptions, "ZMAX", sMax);
1125 : }
1126 25 : if (CSLFetchNameValue(papszOptions, "ZMIN") == nullptr)
1127 : {
1128 25 : CPLsnprintf(sMin, sizeof(sMin), "%f", dfMin);
1129 25 : tmpOptions = CSLSetNameValue(tmpOptions, "ZMIN", sMin);
1130 : }
1131 :
1132 : GDALDriver *poDriver =
1133 25 : GDALDriver::FromHandle(GDALGetDriverByName("NWT_GRD"));
1134 25 : GDALDataset *poDstDS = poDriver->DefaultCreateCopy(
1135 : pszFilename, poSrcDS, bStrict, tmpOptions, pfnProgress, pProgressData);
1136 :
1137 25 : CSLDestroy(tmpOptions);
1138 :
1139 25 : return poDstDS;
1140 : }
1141 : #endif // NO_MITAB_SUPPORT
1142 :
1143 : /************************************************************************/
1144 : /* GDALRegister_GRD() */
1145 : /************************************************************************/
1146 1682 : void GDALRegister_NWT_GRD()
1147 : {
1148 1682 : if (GDALGetDriverByName("NWT_GRD") != nullptr)
1149 301 : return;
1150 :
1151 1381 : GDALDriver *poDriver = new GDALDriver();
1152 :
1153 1381 : poDriver->SetDescription("NWT_GRD");
1154 1381 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1155 1381 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1156 1381 : "Northwood Numeric Grid Format .grd/.tab");
1157 1381 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/nwtgrd.html");
1158 1381 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd");
1159 1381 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1160 :
1161 1381 : poDriver->SetMetadataItem(
1162 : GDAL_DMD_OPENOPTIONLIST,
1163 : "<OpenOptionList>"
1164 : " <Option name='BAND_COUNT' type='int' description='1 (Z) or 4 "
1165 : "(RGBZ). Only used in read-only mode' default='4'/>"
1166 1381 : "</OpenOptionList>");
1167 :
1168 1381 : poDriver->pfnOpen = NWT_GRDDataset::Open;
1169 1381 : poDriver->pfnIdentify = NWT_GRDDataset::Identify;
1170 :
1171 : #ifndef NO_MITAB_SUPPORT
1172 :
1173 1381 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Float32");
1174 1381 : poDriver->SetMetadataItem(
1175 : GDAL_DMD_CREATIONOPTIONLIST,
1176 : "<CreationOptionList>"
1177 : " <Option name='ZMIN' type='float' description='Minimum cell value "
1178 : "of raster for defining RGB scaling' default='-2E+37'/>"
1179 : " <Option name='ZMAX' type='float' description='Maximum cell value "
1180 : "of raster for defining RGB scaling' default='2E+38'/>"
1181 : " <Option name='BRIGHTNESS' type='int' description='Brightness to "
1182 : "be recorded in TAB file. Only affects reading with MapInfo' "
1183 : "default='50'/>"
1184 : " <Option name='CONTRAST' type='int' description='Contrast to be "
1185 : "recorded in TAB file. Only affects reading with MapInfo' "
1186 : "default='50'/>"
1187 : " <Option name='TRANSCOLOR' type='int' description='Transparent "
1188 : "color to be recorded in TAB file. Only affects reading with MapInfo' "
1189 : "default='0'/>"
1190 : " <Option name='TRANSLUCENCY' type='int' description='Level of "
1191 : "translucency to be recorded in TAB file. Only affects reading with "
1192 : "MapInfo' default='0'/>"
1193 1381 : "</CreationOptionList>");
1194 :
1195 1381 : poDriver->pfnCreate = NWT_GRDDataset::Create;
1196 1381 : poDriver->pfnCreateCopy = NWT_GRDDataset::CreateCopy;
1197 : #endif
1198 :
1199 1381 : GetGDALDriverManager()->RegisterDriver(poDriver);
1200 : }
|