/src/gdal/frmts/gff/gff_dataset.cpp
Line | Count | Source |
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() override; |
58 | | |
59 | | static GDALDataset *Open(GDALOpenInfo *); |
60 | | static int Identify(GDALOpenInfo *poOpenInfo); |
61 | | }; |
62 | | |
63 | | GFFDataset::GFFDataset() |
64 | 119 | : fp(nullptr), eDataType(GDT_Unknown), nEndianness(0), nVersionMajor(0), |
65 | 119 | nVersionMinor(0), nLength(0), nBPP(0), nFrameCnt(0), nImageType(0), |
66 | 119 | nRowMajor(0), nRgCnt(0), nAzCnt(0) |
67 | 119 | { |
68 | 119 | } |
69 | | |
70 | | GFFDataset::~GFFDataset() |
71 | 119 | { |
72 | 119 | if (fp != nullptr) |
73 | 119 | VSIFCloseL(fp); |
74 | 119 | } |
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 | 204 | { |
92 | | // Determine the number of bytes per sample. |
93 | 204 | unsigned long nBytes = 1; |
94 | 204 | switch (eDataType) |
95 | 204 | { |
96 | 110 | case GDT_CInt16: |
97 | 110 | nBytes = 4; |
98 | 110 | break; |
99 | 0 | case GDT_CInt32: |
100 | 74 | case GDT_CFloat32: |
101 | 74 | nBytes = 8; |
102 | 74 | break; |
103 | 20 | default: |
104 | 20 | nBytes = 1; |
105 | 204 | } |
106 | | |
107 | 204 | return nBytes; |
108 | 204 | } |
109 | | |
110 | | /************************************************************************/ |
111 | | /* GFFRasterBand() */ |
112 | | /************************************************************************/ |
113 | | GFFRasterBand::GFFRasterBand(GFFDataset *poDSIn, int nBandIn, |
114 | | GDALDataType eDataTypeIn) |
115 | 102 | : nRasterBandMemory(GFFSampleSize(eDataTypeIn) * poDSIn->GetRasterXSize()), |
116 | 102 | nSampleSize(static_cast<int>(GFFSampleSize(eDataTypeIn))) |
117 | 102 | { |
118 | 102 | poDS = poDSIn; |
119 | 102 | nBand = nBandIn; |
120 | | |
121 | 102 | eDataType = eDataTypeIn; |
122 | | |
123 | 102 | nBlockXSize = poDS->GetRasterXSize(); |
124 | 102 | nBlockYSize = 1; |
125 | 102 | } |
126 | | |
127 | | /************************************************************************/ |
128 | | /* IReadBlock() */ |
129 | | /************************************************************************/ |
130 | | |
131 | | CPLErr GFFRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff, |
132 | | void *pImage) |
133 | 2.78k | { |
134 | 2.78k | GFFDataset *poGDS = cpl::down_cast<GFFDataset *>(poDS); |
135 | 2.78k | long nOffset = poGDS->nLength; |
136 | | |
137 | 2.78k | VSIFSeekL(poGDS->fp, |
138 | 2.78k | nOffset + (static_cast<vsi_l_offset>(poGDS->GetRasterXSize()) * |
139 | 2.78k | nBlockYOff * (nSampleSize)), |
140 | 2.78k | SEEK_SET); |
141 | | |
142 | | /* Ingest entire range line */ |
143 | 2.78k | if (VSIFReadL(pImage, nRasterBandMemory, 1, poGDS->fp) != 1) |
144 | 31 | return CE_Failure; |
145 | | |
146 | | #if defined(CPL_MSB) |
147 | | if (GDALDataTypeIsComplex(eDataType)) |
148 | | { |
149 | | int nWordSize = GDALGetDataTypeSize(eDataType) / 16; |
150 | | GDALSwapWords(pImage, nWordSize, nBlockXSize, 2 * nWordSize); |
151 | | GDALSwapWords(((GByte *)pImage) + nWordSize, nWordSize, nBlockXSize, |
152 | | 2 * nWordSize); |
153 | | } |
154 | | #endif |
155 | | |
156 | 2.75k | return CE_None; |
157 | 2.78k | } |
158 | | |
159 | | /******************************************************************** |
160 | | * ================================================================ * |
161 | | * Implementation of the GFFDataset Class * |
162 | | * ================================================================ * |
163 | | ********************************************************************/ |
164 | | |
165 | | /************************************************************************/ |
166 | | /* Identify() */ |
167 | | /************************************************************************/ |
168 | | int GFFDataset::Identify(GDALOpenInfo *poOpenInfo) |
169 | 503k | { |
170 | 503k | if (poOpenInfo->nHeaderBytes < 7) |
171 | 397k | return 0; |
172 | | |
173 | 106k | if (STARTS_WITH_CI((char *)poOpenInfo->pabyHeader, "GSATIMG")) |
174 | 119 | return 1; |
175 | | |
176 | 106k | return 0; |
177 | 106k | } |
178 | | |
179 | | /************************************************************************/ |
180 | | /* Open() */ |
181 | | /************************************************************************/ |
182 | | |
183 | | GDALDataset *GFFDataset::Open(GDALOpenInfo *poOpenInfo) |
184 | 503k | { |
185 | | /* Check that the dataset is indeed a GSAT File Format (GFF) file */ |
186 | 503k | if (!GFFDataset::Identify(poOpenInfo) || poOpenInfo->fpL == nullptr) |
187 | 503k | return nullptr; |
188 | | |
189 | | /* -------------------------------------------------------------------- */ |
190 | | /* Confirm the requested access is supported. */ |
191 | | /* -------------------------------------------------------------------- */ |
192 | 119 | if (poOpenInfo->eAccess == GA_Update) |
193 | 0 | { |
194 | 0 | ReportUpdateNotSupportedByDriver("GFF"); |
195 | 0 | return nullptr; |
196 | 0 | } |
197 | | |
198 | 119 | GFFDataset *poDS = new GFFDataset(); |
199 | | |
200 | 119 | poDS->fp = poOpenInfo->fpL; |
201 | 119 | poOpenInfo->fpL = nullptr; |
202 | | |
203 | | /* Check the endianness of the file */ |
204 | 119 | VSIFSeekL(poDS->fp, 54, SEEK_SET); |
205 | 119 | VSIFReadL(&(poDS->nEndianness), 2, 1, poDS->fp); |
206 | | |
207 | 119 | VSIFSeekL(poDS->fp, 8, SEEK_SET); |
208 | 119 | VSIFReadL(&poDS->nVersionMinor, 2, 1, poDS->fp); |
209 | 119 | CPL_LSBPTR16(&poDS->nVersionMinor); |
210 | 119 | VSIFReadL(&poDS->nVersionMajor, 2, 1, poDS->fp); |
211 | 119 | CPL_LSBPTR16(&poDS->nVersionMajor); |
212 | 119 | VSIFReadL(&poDS->nLength, 4, 1, poDS->fp); |
213 | 119 | CPL_LSBPTR32(&poDS->nLength); |
214 | | |
215 | 119 | unsigned short nCreatorLength = 0; |
216 | 119 | VSIFReadL(&nCreatorLength, 2, 1, poDS->fp); |
217 | 119 | CPL_LSBPTR16(&nCreatorLength); |
218 | | /* Hack for now... I should properly load the date metadata, for |
219 | | * example |
220 | | */ |
221 | 119 | VSIFSeekL(poDS->fp, 56, SEEK_SET); |
222 | | |
223 | | /* By looking at the Matlab code, one should write something like the |
224 | | * following test */ |
225 | | /* but the results don't seem to be the ones really expected */ |
226 | | /*if ((poDS->nVersionMajor == 1 && poDS->nVersionMinor > 7) || |
227 | | (poDS->nVersionMajor > 1)) |
228 | | { |
229 | | float fBPP; |
230 | | VSIFRead(&fBPP,4,1,poDS->fp); |
231 | | poDS->nBPP = fBPP; |
232 | | } |
233 | | else*/ |
234 | 119 | { |
235 | 119 | VSIFReadL(&poDS->nBPP, 4, 1, poDS->fp); |
236 | 119 | CPL_LSBPTR32(&poDS->nBPP); |
237 | 119 | } |
238 | 119 | VSIFReadL(&poDS->nFrameCnt, 4, 1, poDS->fp); |
239 | 119 | CPL_LSBPTR32(&poDS->nFrameCnt); |
240 | 119 | VSIFReadL(&poDS->nImageType, 4, 1, poDS->fp); |
241 | 119 | CPL_LSBPTR32(&poDS->nImageType); |
242 | 119 | VSIFReadL(&poDS->nRowMajor, 4, 1, poDS->fp); |
243 | 119 | CPL_LSBPTR32(&poDS->nRowMajor); |
244 | 119 | VSIFReadL(&poDS->nRgCnt, 4, 1, poDS->fp); |
245 | 119 | CPL_LSBPTR32(&poDS->nRgCnt); |
246 | 119 | VSIFReadL(&poDS->nAzCnt, 4, 1, poDS->fp); |
247 | 119 | CPL_LSBPTR32(&poDS->nAzCnt); |
248 | | |
249 | | /* We now have enough information to determine the number format */ |
250 | 119 | switch (poDS->nImageType) |
251 | 119 | { |
252 | 10 | case 0: |
253 | 10 | poDS->eDataType = GDT_UInt8; |
254 | 10 | break; |
255 | | |
256 | 55 | case 1: |
257 | 55 | if (poDS->nBPP == 4) |
258 | 55 | poDS->eDataType = GDT_CInt16; |
259 | 0 | else |
260 | 0 | poDS->eDataType = GDT_CInt32; |
261 | 55 | break; |
262 | | |
263 | 39 | case 2: |
264 | 39 | poDS->eDataType = GDT_CFloat32; |
265 | 39 | break; |
266 | | |
267 | 15 | default: |
268 | 15 | CPLError(CE_Failure, CPLE_AppDefined, "Unknown image type found!"); |
269 | 15 | delete poDS; |
270 | 15 | return nullptr; |
271 | 119 | } |
272 | | |
273 | | /* Set raster width/height |
274 | | * Note that the images that are complex are listed as having twice the |
275 | | * number of X-direction values than there are actual pixels. This is |
276 | | * because whoever came up with the format was crazy (actually, my |
277 | | * hunch is that they designed it very much for Matlab) |
278 | | * */ |
279 | 104 | if (poDS->nRowMajor) |
280 | 67 | { |
281 | 67 | poDS->nRasterXSize = poDS->nRgCnt / (poDS->nImageType == 0 ? 1 : 2); |
282 | 67 | poDS->nRasterYSize = poDS->nAzCnt; |
283 | 67 | } |
284 | 37 | else |
285 | 37 | { |
286 | 37 | poDS->nRasterXSize = poDS->nAzCnt / (poDS->nImageType == 0 ? 1 : 2); |
287 | 37 | poDS->nRasterYSize = poDS->nRgCnt; |
288 | 37 | } |
289 | | |
290 | 104 | if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0) |
291 | 2 | { |
292 | 2 | CPLError(CE_Failure, CPLE_AppDefined, |
293 | 2 | "Invalid raster dimensions : %d x %d", poDS->nRasterXSize, |
294 | 2 | poDS->nRasterYSize); |
295 | 2 | delete poDS; |
296 | 2 | return nullptr; |
297 | 2 | } |
298 | | |
299 | 102 | poDS->SetBand(1, new GFFRasterBand(poDS, 1, poDS->eDataType)); |
300 | | |
301 | | /* -------------------------------------------------------------------- */ |
302 | | /* Initialize any PAM information. */ |
303 | | /* -------------------------------------------------------------------- */ |
304 | 102 | poDS->SetDescription(poOpenInfo->pszFilename); |
305 | 102 | poDS->TryLoadXML(); |
306 | | |
307 | | /* -------------------------------------------------------------------- */ |
308 | | /* Support overviews. */ |
309 | | /* -------------------------------------------------------------------- */ |
310 | 102 | poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename); |
311 | | |
312 | 102 | return poDS; |
313 | 104 | } |
314 | | |
315 | | /************************************************************************/ |
316 | | /* GDALRegister_GFF() */ |
317 | | /************************************************************************/ |
318 | | |
319 | | void GDALRegister_GFF() |
320 | 22 | { |
321 | 22 | if (GDALGetDriverByName("GFF") != nullptr) |
322 | 0 | return; |
323 | | |
324 | 22 | GDALDriver *poDriver = new GDALDriver(); |
325 | | |
326 | 22 | poDriver->SetDescription("GFF"); |
327 | 22 | poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); |
328 | 22 | poDriver->SetMetadataItem( |
329 | 22 | GDAL_DMD_LONGNAME, |
330 | 22 | "Ground-based SAR Applications Testbed File Format (.gff)"); |
331 | 22 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gff.html"); |
332 | 22 | poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gff"); |
333 | 22 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
334 | 22 | poDriver->pfnOpen = GFFDataset::Open; |
335 | 22 | GetGDALDriverManager()->RegisterDriver(poDriver); |
336 | 22 | } |