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