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