/src/gdal/frmts/gff/gff_dataset.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: Ground-based SAR Applitcations Testbed File Format driver |
4 | | * Purpose: Support in GDAL for Sandia National Laboratory's GFF format |
5 | | * blame Tisham for putting me up to this |
6 | | * Author: Philippe Vachon <philippe@cowpig.ca> |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2007, Philippe Vachon |
10 | | * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com> |
11 | | * |
12 | | * SPDX-License-Identifier: MIT |
13 | | ****************************************************************************/ |
14 | | |
15 | | #include "cpl_conv.h" |
16 | | #include "cpl_port.h" |
17 | | #include "cpl_string.h" |
18 | | #include "cpl_vsi.h" |
19 | | #include "gdal_frmts.h" |
20 | | #include "gdal_pam.h" |
21 | | #include "gdal_priv.h" |
22 | | |
23 | | /******************************************************************* |
24 | | * Declaration of the GFFDataset class * |
25 | | *******************************************************************/ |
26 | | |
27 | | class GFFRasterBand; |
28 | | |
29 | | class GFFDataset final : public GDALPamDataset |
30 | | { |
31 | | friend class GFFRasterBand; |
32 | | VSILFILE *fp; |
33 | | GDALDataType eDataType; |
34 | | unsigned int nEndianness; |
35 | | /* Some relevant headers */ |
36 | | unsigned short nVersionMajor; |
37 | | unsigned short nVersionMinor; |
38 | | unsigned int nLength; |
39 | | // char *pszCreator; |
40 | | // TODO: Needs a better explanation. |
41 | | /* I am taking this at face value (are they insane?) */ |
42 | | // float fBPP; |
43 | | unsigned int nBPP; |
44 | | |
45 | | /* Good information to know */ |
46 | | unsigned int nFrameCnt; |
47 | | unsigned int nImageType; |
48 | | unsigned int nRowMajor; |
49 | | unsigned int nRgCnt; |
50 | | unsigned int nAzCnt; |
51 | | // long nScaleExponent; |
52 | | // long nScaleMantissa; |
53 | | // long nOffsetExponent; |
54 | | // long nOffsetMantissa; |
55 | | public: |
56 | | GFFDataset(); |
57 | | ~GFFDataset(); |
58 | | |
59 | | static GDALDataset *Open(GDALOpenInfo *); |
60 | | static int Identify(GDALOpenInfo *poOpenInfo); |
61 | | }; |
62 | | |
63 | | GFFDataset::GFFDataset() |
64 | 0 | : fp(nullptr), eDataType(GDT_Unknown), nEndianness(0), nVersionMajor(0), |
65 | 0 | nVersionMinor(0), nLength(0), nBPP(0), nFrameCnt(0), nImageType(0), |
66 | 0 | nRowMajor(0), nRgCnt(0), nAzCnt(0) |
67 | 0 | { |
68 | 0 | } |
69 | | |
70 | | GFFDataset::~GFFDataset() |
71 | 0 | { |
72 | 0 | if (fp != nullptr) |
73 | 0 | VSIFCloseL(fp); |
74 | 0 | } |
75 | | |
76 | | /********************************************************************* |
77 | | * Declaration and implementation of the GFFRasterBand Class * |
78 | | *********************************************************************/ |
79 | | |
80 | | class GFFRasterBand final : public GDALPamRasterBand |
81 | | { |
82 | | long nRasterBandMemory; |
83 | | int nSampleSize; |
84 | | |
85 | | public: |
86 | | GFFRasterBand(GFFDataset *, int, GDALDataType); |
87 | | CPLErr IReadBlock(int, int, void *) override; |
88 | | }; |
89 | | |
90 | | static unsigned long GFFSampleSize(GDALDataType eDataType) |
91 | 0 | { |
92 | | // Determine the number of bytes per sample. |
93 | 0 | unsigned long nBytes = 1; |
94 | 0 | switch (eDataType) |
95 | 0 | { |
96 | 0 | case GDT_CInt16: |
97 | 0 | nBytes = 4; |
98 | 0 | break; |
99 | 0 | case GDT_CInt32: |
100 | 0 | case GDT_CFloat32: |
101 | 0 | nBytes = 8; |
102 | 0 | break; |
103 | 0 | default: |
104 | 0 | nBytes = 1; |
105 | 0 | } |
106 | | |
107 | 0 | return nBytes; |
108 | 0 | } |
109 | | |
110 | | /************************************************************************/ |
111 | | /* GFFRasterBand() */ |
112 | | /************************************************************************/ |
113 | | GFFRasterBand::GFFRasterBand(GFFDataset *poDSIn, int nBandIn, |
114 | | GDALDataType eDataTypeIn) |
115 | 0 | : nRasterBandMemory(GFFSampleSize(eDataTypeIn) * poDSIn->GetRasterXSize()), |
116 | 0 | nSampleSize(static_cast<int>(GFFSampleSize(eDataTypeIn))) |
117 | 0 | { |
118 | 0 | poDS = poDSIn; |
119 | 0 | nBand = nBandIn; |
120 | |
|
121 | 0 | eDataType = eDataTypeIn; |
122 | |
|
123 | 0 | nBlockXSize = poDS->GetRasterXSize(); |
124 | 0 | nBlockYSize = 1; |
125 | 0 | } |
126 | | |
127 | | /************************************************************************/ |
128 | | /* IReadBlock() */ |
129 | | /************************************************************************/ |
130 | | |
131 | | CPLErr GFFRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff, |
132 | | void *pImage) |
133 | 0 | { |
134 | 0 | GFFDataset *poGDS = cpl::down_cast<GFFDataset *>(poDS); |
135 | 0 | long nOffset = poGDS->nLength; |
136 | |
|
137 | 0 | VSIFSeekL(poGDS->fp, |
138 | 0 | nOffset + (poGDS->GetRasterXSize() * nBlockYOff * (nSampleSize)), |
139 | 0 | SEEK_SET); |
140 | | |
141 | | /* Ingest entire range line */ |
142 | 0 | if (VSIFReadL(pImage, nRasterBandMemory, 1, poGDS->fp) != 1) |
143 | 0 | return CE_Failure; |
144 | | |
145 | | #if defined(CPL_MSB) |
146 | | if (GDALDataTypeIsComplex(eDataType)) |
147 | | { |
148 | | int nWordSize = GDALGetDataTypeSize(eDataType) / 16; |
149 | | GDALSwapWords(pImage, nWordSize, nBlockXSize, 2 * nWordSize); |
150 | | GDALSwapWords(((GByte *)pImage) + nWordSize, nWordSize, nBlockXSize, |
151 | | 2 * nWordSize); |
152 | | } |
153 | | #endif |
154 | | |
155 | 0 | return CE_None; |
156 | 0 | } |
157 | | |
158 | | /******************************************************************** |
159 | | * ================================================================ * |
160 | | * Implementation of the GFFDataset Class * |
161 | | * ================================================================ * |
162 | | ********************************************************************/ |
163 | | |
164 | | /************************************************************************/ |
165 | | /* Identify() */ |
166 | | /************************************************************************/ |
167 | | int GFFDataset::Identify(GDALOpenInfo *poOpenInfo) |
168 | 16.3k | { |
169 | 16.3k | if (poOpenInfo->nHeaderBytes < 7) |
170 | 571 | return 0; |
171 | | |
172 | 15.7k | if (STARTS_WITH_CI((char *)poOpenInfo->pabyHeader, "GSATIMG")) |
173 | 0 | return 1; |
174 | | |
175 | 15.7k | return 0; |
176 | 15.7k | } |
177 | | |
178 | | /************************************************************************/ |
179 | | /* Open() */ |
180 | | /************************************************************************/ |
181 | | |
182 | | GDALDataset *GFFDataset::Open(GDALOpenInfo *poOpenInfo) |
183 | 16.3k | { |
184 | | /* Check that the dataset is indeed a GSAT File Format (GFF) file */ |
185 | 16.3k | if (!GFFDataset::Identify(poOpenInfo) || poOpenInfo->fpL == nullptr) |
186 | 16.3k | return nullptr; |
187 | | |
188 | | /* -------------------------------------------------------------------- */ |
189 | | /* Confirm the requested access is supported. */ |
190 | | /* -------------------------------------------------------------------- */ |
191 | 0 | if (poOpenInfo->eAccess == GA_Update) |
192 | 0 | { |
193 | 0 | ReportUpdateNotSupportedByDriver("GFF"); |
194 | 0 | return nullptr; |
195 | 0 | } |
196 | | |
197 | 0 | GFFDataset *poDS = new GFFDataset(); |
198 | |
|
199 | 0 | poDS->fp = poOpenInfo->fpL; |
200 | 0 | poOpenInfo->fpL = nullptr; |
201 | | |
202 | | /* Check the endianness of the file */ |
203 | 0 | VSIFSeekL(poDS->fp, 54, SEEK_SET); |
204 | 0 | VSIFReadL(&(poDS->nEndianness), 2, 1, poDS->fp); |
205 | |
|
206 | 0 | VSIFSeekL(poDS->fp, 8, SEEK_SET); |
207 | 0 | VSIFReadL(&poDS->nVersionMinor, 2, 1, poDS->fp); |
208 | 0 | CPL_LSBPTR16(&poDS->nVersionMinor); |
209 | 0 | VSIFReadL(&poDS->nVersionMajor, 2, 1, poDS->fp); |
210 | 0 | CPL_LSBPTR16(&poDS->nVersionMajor); |
211 | 0 | VSIFReadL(&poDS->nLength, 4, 1, poDS->fp); |
212 | 0 | CPL_LSBPTR32(&poDS->nLength); |
213 | |
|
214 | 0 | unsigned short nCreatorLength = 0; |
215 | 0 | VSIFReadL(&nCreatorLength, 2, 1, poDS->fp); |
216 | 0 | CPL_LSBPTR16(&nCreatorLength); |
217 | | /* Hack for now... I should properly load the date metadata, for |
218 | | * example |
219 | | */ |
220 | 0 | VSIFSeekL(poDS->fp, 56, SEEK_SET); |
221 | | |
222 | | /* By looking at the Matlab code, one should write something like the |
223 | | * following test */ |
224 | | /* but the results don't seem to be the ones really expected */ |
225 | | /*if ((poDS->nVersionMajor == 1 && poDS->nVersionMinor > 7) || |
226 | | (poDS->nVersionMajor > 1)) |
227 | | { |
228 | | float fBPP; |
229 | | VSIFRead(&fBPP,4,1,poDS->fp); |
230 | | poDS->nBPP = fBPP; |
231 | | } |
232 | | else*/ |
233 | 0 | { |
234 | 0 | VSIFReadL(&poDS->nBPP, 4, 1, poDS->fp); |
235 | 0 | CPL_LSBPTR32(&poDS->nBPP); |
236 | 0 | } |
237 | 0 | VSIFReadL(&poDS->nFrameCnt, 4, 1, poDS->fp); |
238 | 0 | CPL_LSBPTR32(&poDS->nFrameCnt); |
239 | 0 | VSIFReadL(&poDS->nImageType, 4, 1, poDS->fp); |
240 | 0 | CPL_LSBPTR32(&poDS->nImageType); |
241 | 0 | VSIFReadL(&poDS->nRowMajor, 4, 1, poDS->fp); |
242 | 0 | CPL_LSBPTR32(&poDS->nRowMajor); |
243 | 0 | VSIFReadL(&poDS->nRgCnt, 4, 1, poDS->fp); |
244 | 0 | CPL_LSBPTR32(&poDS->nRgCnt); |
245 | 0 | VSIFReadL(&poDS->nAzCnt, 4, 1, poDS->fp); |
246 | 0 | CPL_LSBPTR32(&poDS->nAzCnt); |
247 | | |
248 | | /* We now have enough information to determine the number format */ |
249 | 0 | switch (poDS->nImageType) |
250 | 0 | { |
251 | 0 | case 0: |
252 | 0 | poDS->eDataType = GDT_Byte; |
253 | 0 | break; |
254 | | |
255 | 0 | case 1: |
256 | 0 | if (poDS->nBPP == 4) |
257 | 0 | poDS->eDataType = GDT_CInt16; |
258 | 0 | else |
259 | 0 | poDS->eDataType = GDT_CInt32; |
260 | 0 | break; |
261 | | |
262 | 0 | case 2: |
263 | 0 | poDS->eDataType = GDT_CFloat32; |
264 | 0 | break; |
265 | | |
266 | 0 | default: |
267 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unknown image type found!"); |
268 | 0 | delete poDS; |
269 | 0 | return nullptr; |
270 | 0 | } |
271 | | |
272 | | /* Set raster width/height |
273 | | * Note that the images that are complex are listed as having twice the |
274 | | * number of X-direction values than there are actual pixels. This is |
275 | | * because whoever came up with the format was crazy (actually, my |
276 | | * hunch is that they designed it very much for Matlab) |
277 | | * */ |
278 | 0 | if (poDS->nRowMajor) |
279 | 0 | { |
280 | 0 | poDS->nRasterXSize = poDS->nRgCnt / (poDS->nImageType == 0 ? 1 : 2); |
281 | 0 | poDS->nRasterYSize = poDS->nAzCnt; |
282 | 0 | } |
283 | 0 | else |
284 | 0 | { |
285 | 0 | poDS->nRasterXSize = poDS->nAzCnt / (poDS->nImageType == 0 ? 1 : 2); |
286 | 0 | poDS->nRasterYSize = poDS->nRgCnt; |
287 | 0 | } |
288 | |
|
289 | 0 | if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0) |
290 | 0 | { |
291 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
292 | 0 | "Invalid raster dimensions : %d x %d", poDS->nRasterXSize, |
293 | 0 | poDS->nRasterYSize); |
294 | 0 | delete poDS; |
295 | 0 | return nullptr; |
296 | 0 | } |
297 | | |
298 | 0 | poDS->SetBand(1, new GFFRasterBand(poDS, 1, poDS->eDataType)); |
299 | | |
300 | | /* -------------------------------------------------------------------- */ |
301 | | /* Initialize any PAM information. */ |
302 | | /* -------------------------------------------------------------------- */ |
303 | 0 | poDS->SetDescription(poOpenInfo->pszFilename); |
304 | 0 | poDS->TryLoadXML(); |
305 | | |
306 | | /* -------------------------------------------------------------------- */ |
307 | | /* Support overviews. */ |
308 | | /* -------------------------------------------------------------------- */ |
309 | 0 | poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename); |
310 | |
|
311 | 0 | return poDS; |
312 | 0 | } |
313 | | |
314 | | /************************************************************************/ |
315 | | /* GDALRegister_GFF() */ |
316 | | /************************************************************************/ |
317 | | |
318 | | void GDALRegister_GFF() |
319 | 2 | { |
320 | 2 | if (GDALGetDriverByName("GFF") != nullptr) |
321 | 0 | return; |
322 | | |
323 | 2 | GDALDriver *poDriver = new GDALDriver(); |
324 | | |
325 | 2 | poDriver->SetDescription("GFF"); |
326 | 2 | poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); |
327 | 2 | poDriver->SetMetadataItem( |
328 | 2 | GDAL_DMD_LONGNAME, |
329 | 2 | "Ground-based SAR Applications Testbed File Format (.gff)"); |
330 | 2 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gff.html"); |
331 | 2 | poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gff"); |
332 | 2 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
333 | 2 | poDriver->pfnOpen = GFFDataset::Open; |
334 | 2 | GetGDALDriverManager()->RegisterDriver(poDriver); |
335 | 2 | } |