Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: IRIS Reader
4 : * Purpose: All code for IRIS format Reader
5 : * Author: Roger Veciana, rveciana@gmail.com
6 : * Portions are adapted from code copyright (C) 2005-2012
7 : * Chris Veness under a CC-BY 3.0 licence
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2012, Roger Veciana <rveciana@gmail.com>
11 : * Copyright (c) 2012-2013, Even Rouault <even dot rouault at spatialys.com>
12 : *
13 : * Permission is hereby granted, free of charge, to any person obtaining a
14 : * copy of this software and associated documentation files (the "Software"),
15 : * to deal in the Software without restriction, including without limitation
16 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 : * and/or sell copies of the Software, and to permit persons to whom the
18 : * Software is furnished to do so, subject to the following conditions:
19 : *
20 : * The above copyright notice and this permission notice shall be included
21 : * in all copies or substantial portions of the Software.
22 : *
23 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 : * DEALINGS IN THE SOFTWARE.
30 : ****************************************************************************/
31 :
32 : #include "cpl_port.h"
33 : #include "gdal_frmts.h"
34 : #include "gdal_pam.h"
35 : #include "ogr_spatialref.h"
36 :
37 : #include <algorithm>
38 : #include <sstream>
39 :
40 : static double DEG2RAD = M_PI / 180.0;
41 : static double RAD2DEG = 180.0 / M_PI;
42 :
43 : /************************************************************************/
44 : /* ==================================================================== */
45 : /* IRISDataset */
46 : /* ==================================================================== */
47 : /************************************************************************/
48 :
49 : class IRISRasterBand;
50 :
51 : class IRISDataset final : public GDALPamDataset
52 : {
53 : friend class IRISRasterBand;
54 :
55 : VSILFILE *fp;
56 : GByte abyHeader[640];
57 : bool bNoDataSet;
58 : double dfNoDataValue;
59 : static const char *const aszProductNames[];
60 : static const char *const aszDataTypeCodes[];
61 : static const char *const aszDataTypes[];
62 : static const char *const aszProjections[];
63 : unsigned short nProductCode;
64 : unsigned short nDataTypeCode;
65 : unsigned char nProjectionCode;
66 : float fNyquistVelocity;
67 : mutable OGRSpatialReference m_oSRS{};
68 : mutable double adfGeoTransform[6];
69 : mutable bool bHasLoadedProjection;
70 : void LoadProjection() const;
71 : static bool GeodesicCalculation(double fLat, double fLon, double fAngle,
72 : double fDist, double fEquatorialRadius,
73 : double fPolarRadius, double fFlattening,
74 : std::pair<double, double> &oOutPair);
75 :
76 : public:
77 : IRISDataset();
78 : virtual ~IRISDataset();
79 :
80 : static GDALDataset *Open(GDALOpenInfo *);
81 : static int Identify(GDALOpenInfo *);
82 :
83 : CPLErr GetGeoTransform(double *padfTransform) override;
84 : const OGRSpatialReference *GetSpatialRef() const override;
85 : };
86 :
87 : const char *const IRISDataset::aszProductNames[] = {
88 : "", "PPI", "RHI", "CAPPI", "CROSS", "TOPS", "TRACK",
89 : "RAIN1", "RAINN", "VVP", "VIL", "SHEAR", "WARN", "CATCH",
90 : "RTI", "RAW", "MAX", "USER", "USERV", "OTHER", "STATUS",
91 : "SLINE", "WIND", "BEAM", "TEXT", "FCAST", "NDOP", "IMAGE",
92 : "COMP", "TDWR", "GAGE", "DWELL", "SRI", "BASE", "HMAX"};
93 :
94 : const char *const IRISDataset::aszDataTypeCodes[] = {
95 : "XHDR", "DBT", "dBZ", "VEL", "WIDTH", "ZDR",
96 : "ORAIN", "dBZC", "DBT2", "dBZ2", "VEL2", "WIDTH2",
97 : "ZDR2", "RAINRATE2", "KDP", "KDP2", "PHIDP", "VELC",
98 : "SQI", "RHOHV", "RHOHV2", "dBZC2", "VELC2", "SQI2",
99 : "PHIDP2", "LDRH", "LDRH2", "LDRV", "LDRV2", "FLAGS",
100 : "FLAGS2", "FLOAT32", "HEIGHT", "VIL2", "NULL", "SHEAR",
101 : "DIVERGE2", "FLIQUID2", "USER", "OTHER", "DEFORM2", "VVEL2",
102 : "HVEL2", "HDIR2", "AXDIL2", "TIME2", "RHOH", "RHOH2",
103 : "RHOV", "RHOV2", "PHIH", "PHIH2", "PHIV", "PHIV2",
104 : "USER2", "HCLASS", "HCLASS2", "ZDRC", "ZDRC2", "TEMPERATURE16",
105 : "VIR16", "DBTV8", "DBTV16", "DBZV8", "DBZV16", "SNR8",
106 : "SNR16", "ALBEDO8", "ALBEDO16", "VILD16", "TURB16"};
107 :
108 : const char *const IRISDataset::aszDataTypes[] = {
109 : "Extended Headers",
110 : "Total H power (1 byte)",
111 : "Clutter Corrected H reflectivity (1 byte)",
112 : "Velocity (1 byte)",
113 : "Width (1 byte)",
114 : "Differential reflectivity (1 byte)",
115 : "Old Rainfall rate (stored as dBZ)",
116 : "Fully corrected reflectivity (1 byte)",
117 : "Uncorrected reflectivity (2 byte)",
118 : "Corrected reflectivity (2 byte)",
119 : "Velocity (2 byte)",
120 : "Width (2 byte)",
121 : "Differential reflectivity (2 byte)",
122 : "Rainfall rate (2 byte)",
123 : "Kdp (specific differential phase)(1 byte)",
124 : "Kdp (specific differential phase)(2 byte)",
125 : "PHIdp (differential phase)(1 byte)",
126 : "Corrected Velocity (1 byte)",
127 : "SQI (1 byte)",
128 : "RhoHV(0) (1 byte)",
129 : "RhoHV(0) (2 byte)",
130 : "Fully corrected reflectivity (2 byte)",
131 : "Corrected Velocity (2 byte)",
132 : "SQI (2 byte)",
133 : "PHIdp (differential phase)(2 byte)",
134 : "LDR H to V (1 byte)",
135 : "LDR H to V (2 byte)",
136 : "LDR V to H (1 byte)",
137 : "LDR V to H (2 byte)",
138 : "Individual flag bits for each bin",
139 : "",
140 : "Test of floating format",
141 : "Height (1/10 km) (1 byte)",
142 : "Linear liquid (.001mm) (2 byte)",
143 : "Data type is not applicable",
144 : "Wind Shear (1 byte)",
145 : "Divergence (.001 10**-4) (2-byte)",
146 : "Floated liquid (2 byte)",
147 : "User type, unspecified data (1 byte)",
148 : "Unspecified data, no color legend",
149 : "Deformation (.001 10**-4) (2-byte)",
150 : "Vertical velocity (.01 m/s) (2-byte)",
151 : "Horizontal velocity (.01 m/s) (2-byte)",
152 : "Horizontal wind direction (.1 degree) (2-byte)",
153 : "Axis of Dillitation (.1 degree) (2-byte)",
154 : "Time of data (seconds) (2-byte)",
155 : "Rho H to V (1 byte)",
156 : "Rho H to V (2 byte)",
157 : "Rho V to H (1 byte)",
158 : "Rho V to H (2 byte)",
159 : "Phi H to V (1 byte)",
160 : "Phi H to V (2 byte)",
161 : "Phi V to H (1 byte)",
162 : "Phi V to H (2 byte)",
163 : "User type, unspecified data (2 byte)",
164 : "Hydrometeor class (1 byte)",
165 : "Hydrometeor class (2 byte)",
166 : "Corrected Differential reflectivity (1 byte)",
167 : "Corrected Differential reflectivity (2 byte)",
168 : "Temperature (2 byte)",
169 : "Vertically Integrated Reflectivity (2 byte)",
170 : "Total V Power (1 byte)",
171 : "Total V Power (2 byte)",
172 : "Clutter Corrected V Reflectivity (1 byte)",
173 : "Clutter Corrected V Reflectivity (2 byte)",
174 : "Signal to Noise ratio (1 byte)",
175 : "Signal to Noise ratio (2 byte)",
176 : "Albedo (1 byte)",
177 : "Albedo (2 byte)",
178 : "VIL Density (2 byte)",
179 : "Turbulence (2 byte)"};
180 :
181 : const char *const IRISDataset::aszProjections[] = {
182 : "Azimutal equidistant", "Mercator", "Polar Stereographic", "UTM",
183 : // FIXME: is it a typo here or in IRIS itself: Perspective or Prespective ?
184 : "Perspective from geosync", "Equidistant cylindrical", "Gnomonic",
185 : "Gauss conformal", "Lambert conformal conic"};
186 :
187 : /************************************************************************/
188 : /* ==================================================================== */
189 : /* IRISRasterBand */
190 : /* ==================================================================== */
191 : /************************************************************************/
192 :
193 : class IRISRasterBand final : public GDALPamRasterBand
194 : {
195 : friend class IRISDataset;
196 :
197 : unsigned char *pszRecord;
198 : bool bBufferAllocFailed;
199 :
200 : public:
201 : IRISRasterBand(IRISDataset *, int);
202 : virtual ~IRISRasterBand();
203 :
204 : virtual CPLErr IReadBlock(int, int, void *) override;
205 :
206 : virtual double GetNoDataValue(int *) override;
207 : virtual CPLErr SetNoDataValue(double) override;
208 : };
209 :
210 : /************************************************************************/
211 : /* IRISRasterBand() */
212 : /************************************************************************/
213 :
214 3 : IRISRasterBand::IRISRasterBand(IRISDataset *poDSIn, int nBandIn)
215 3 : : pszRecord(nullptr), bBufferAllocFailed(false)
216 : {
217 3 : poDS = poDSIn;
218 3 : nBand = nBandIn;
219 3 : eDataType = GDT_Float32;
220 3 : nBlockXSize = poDS->GetRasterXSize();
221 3 : nBlockYSize = 1;
222 3 : }
223 :
224 6 : IRISRasterBand::~IRISRasterBand()
225 : {
226 3 : VSIFree(pszRecord);
227 6 : }
228 :
229 : /************************************************************************/
230 : /* IReadBlock() */
231 : /************************************************************************/
232 :
233 263 : CPLErr IRISRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
234 : void *pImage)
235 :
236 : {
237 263 : IRISDataset *poGDS = static_cast<IRISDataset *>(poDS);
238 :
239 : // Every product type has its own size. TODO: Move it like dataType.
240 263 : int nDataLength = 1;
241 263 : if (poGDS->nDataTypeCode == 2)
242 263 : nDataLength = 1;
243 0 : else if (poGDS->nDataTypeCode == 8)
244 0 : nDataLength = 2;
245 0 : else if (poGDS->nDataTypeCode == 9)
246 0 : nDataLength = 2;
247 0 : else if (poGDS->nDataTypeCode == 37)
248 0 : nDataLength = 2;
249 0 : else if (poGDS->nDataTypeCode == 33)
250 0 : nDataLength = 2;
251 0 : else if (poGDS->nDataTypeCode == 32)
252 0 : nDataLength = 1;
253 :
254 : // We allocate space for storing a record:
255 263 : if (pszRecord == nullptr)
256 : {
257 2 : if (bBufferAllocFailed)
258 0 : return CE_Failure;
259 :
260 2 : pszRecord = static_cast<unsigned char *>(
261 2 : VSI_MALLOC_VERBOSE(static_cast<size_t>(nBlockXSize) * nDataLength));
262 :
263 2 : if (pszRecord == nullptr)
264 : {
265 0 : bBufferAllocFailed = true;
266 0 : return CE_Failure;
267 : }
268 : }
269 :
270 : // Prepare to read (640 is the header size in bytes) and read (the
271 : // y axis in the IRIS files in the inverse direction). The
272 : // previous bands are also added as an offset
273 :
274 263 : VSIFSeekL(poGDS->fp,
275 : 640 +
276 526 : static_cast<vsi_l_offset>(nDataLength) *
277 263 : poGDS->GetRasterXSize() * poGDS->GetRasterYSize() *
278 526 : (this->nBand - 1) +
279 526 : static_cast<vsi_l_offset>(nBlockXSize) * nDataLength *
280 263 : (poGDS->GetRasterYSize() - 1 - nBlockYOff),
281 : SEEK_SET);
282 :
283 263 : if (static_cast<int>(
284 263 : VSIFReadL(pszRecord, static_cast<size_t>(nBlockXSize) * nDataLength,
285 263 : 1, poGDS->fp)) != 1)
286 0 : return CE_Failure;
287 :
288 : // If datatype is dbZ or dBT:
289 : // See point 3.3.3 at page 3.33 of the manual.
290 263 : if (poGDS->nDataTypeCode == 2 || poGDS->nDataTypeCode == 1)
291 : {
292 68384 : for (int i = 0; i < nBlockXSize; i++)
293 : {
294 68121 : float fVal = ((*(pszRecord + i * nDataLength)) - 64.0f) / 2.0f;
295 68121 : if (fVal == 95.5f)
296 0 : fVal = -9999.0f;
297 68121 : ((float *)pImage)[i] = fVal;
298 263 : }
299 : // If datatype is dbZ2 or dBT2:
300 : // See point 3.3.4 at page 3.33 of the manual.
301 : }
302 0 : else if (poGDS->nDataTypeCode == 8 || poGDS->nDataTypeCode == 9)
303 : {
304 0 : for (int i = 0; i < nBlockXSize; i++)
305 : {
306 0 : float fVal =
307 0 : (CPL_LSBUINT16PTR(pszRecord + i * nDataLength) - 32768.0f) /
308 : 100.0f;
309 0 : if (fVal == 327.67f)
310 0 : fVal = -9999.0f;
311 0 : ((float *)pImage)[i] = fVal;
312 0 : }
313 : // Fliquid2 (Rain1 & Rainn products)
314 : // See point 3.3.11 at page 3.43 of the manual.
315 : }
316 0 : else if (poGDS->nDataTypeCode == 37)
317 : {
318 0 : for (int i = 0; i < nBlockXSize; i++)
319 : {
320 0 : const unsigned short nVal =
321 0 : CPL_LSBUINT16PTR(pszRecord + i * nDataLength);
322 0 : const unsigned short nExp = nVal >> 12;
323 0 : const unsigned short nMantissa = nVal - (nExp << 12);
324 0 : float fVal2 = 0.0f;
325 0 : if (nVal == 65535)
326 0 : fVal2 = -9999.0f;
327 0 : else if (nExp == 0)
328 0 : fVal2 = nMantissa / 1000.0f;
329 : else
330 0 : fVal2 = ((nMantissa + 4096) << (nExp - 1)) / 1000.0f;
331 0 : ((float *)pImage)[i] = fVal2;
332 : }
333 : // VIL2 (VIL products)
334 : // See point 3.3.41 at page 3.54 of the manual.
335 : }
336 0 : else if (poGDS->nDataTypeCode == 33)
337 : {
338 0 : for (int i = 0; i < nBlockXSize; i++)
339 : {
340 0 : float fVal = static_cast<float>(
341 0 : CPL_LSBUINT16PTR(pszRecord + i * nDataLength));
342 0 : if (fVal == 65535.0f)
343 0 : ((float *)pImage)[i] = -9999.0f;
344 0 : else if (fVal == 0.0f)
345 0 : ((float *)pImage)[i] = -1.0f;
346 : else
347 0 : ((float *)pImage)[i] = (fVal - 1) / 1000.0f;
348 : }
349 : // HEIGHT (TOPS products)
350 : // See point 3.3.14 at page 3.46 of the manual.
351 : }
352 0 : else if (poGDS->nDataTypeCode == 32)
353 : {
354 0 : for (int i = 0; i < nBlockXSize; i++)
355 : {
356 0 : unsigned char nVal = *(pszRecord + i * nDataLength);
357 0 : if (nVal == 255)
358 0 : ((float *)pImage)[i] = -9999.0f;
359 0 : else if (nVal == 0)
360 0 : ((float *)pImage)[i] = -1.0f;
361 : else
362 0 : ((float *)pImage)[i] = (nVal - 1.0f) / 10.0f;
363 : }
364 : // VEL (Velocity 1-Byte in PPI & others)
365 : // See point 3.3.37 at page 3.53 of the manual.
366 : }
367 0 : else if (poGDS->nDataTypeCode == 3)
368 : {
369 0 : for (int i = 0; i < nBlockXSize; i++)
370 : {
371 0 : float fVal = static_cast<float>(*(pszRecord + i * nDataLength));
372 0 : if (fVal == 0.0f)
373 0 : fVal = -9997.0f;
374 0 : else if (fVal == 1.0f)
375 0 : fVal = -9998.0f;
376 0 : else if (fVal == 255.0f)
377 0 : fVal = -9999.0f;
378 : else
379 0 : fVal = poGDS->fNyquistVelocity * (fVal - 128.0f) / 127.0f;
380 0 : ((float *)pImage)[i] = fVal;
381 : }
382 : // SHEAR (1-Byte Shear)
383 : // See point 3.3.23 at page 3.39 of the manual.
384 : }
385 0 : else if (poGDS->nDataTypeCode == 35)
386 : {
387 0 : for (int i = 0; i < nBlockXSize; i++)
388 : {
389 0 : float fVal = static_cast<float>(*(pszRecord + i * nDataLength));
390 0 : if (fVal == 0.0f)
391 0 : fVal = -9998.0f;
392 0 : else if (fVal == 255.0f)
393 0 : fVal = -9999.0f;
394 : else
395 0 : fVal = (fVal - 128.0f) * 0.2f;
396 0 : ((float *)pImage)[i] = fVal;
397 : }
398 : }
399 :
400 263 : return CE_None;
401 : }
402 :
403 : /************************************************************************/
404 : /* SetNoDataValue() */
405 : /************************************************************************/
406 :
407 3 : CPLErr IRISRasterBand::SetNoDataValue(double dfNoData)
408 :
409 : {
410 3 : IRISDataset *poGDS = static_cast<IRISDataset *>(poDS);
411 :
412 3 : poGDS->bNoDataSet = true;
413 3 : poGDS->dfNoDataValue = dfNoData;
414 :
415 3 : return CE_None;
416 : }
417 :
418 : /************************************************************************/
419 : /* GetNoDataValue() */
420 : /************************************************************************/
421 :
422 0 : double IRISRasterBand::GetNoDataValue(int *pbSuccess)
423 :
424 : {
425 0 : IRISDataset *poGDS = static_cast<IRISDataset *>(poDS);
426 :
427 0 : if (poGDS->bNoDataSet)
428 : {
429 0 : if (pbSuccess)
430 0 : *pbSuccess = TRUE;
431 :
432 0 : return poGDS->dfNoDataValue;
433 : }
434 :
435 0 : return GDALPamRasterBand::GetNoDataValue(pbSuccess);
436 : }
437 :
438 : /************************************************************************/
439 : /* ==================================================================== */
440 : /* IRISDataset */
441 : /* ==================================================================== */
442 : /************************************************************************/
443 :
444 : /************************************************************************/
445 : /* IRISDataset() */
446 : /************************************************************************/
447 :
448 3 : IRISDataset::IRISDataset()
449 : : fp(nullptr), bNoDataSet(false), dfNoDataValue(0.0), nProductCode(0),
450 : nDataTypeCode(0), nProjectionCode(0), fNyquistVelocity(0.0),
451 3 : bHasLoadedProjection(false)
452 : {
453 3 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
454 3 : std::fill_n(abyHeader, CPL_ARRAYSIZE(abyHeader), static_cast<GByte>(0));
455 3 : adfGeoTransform[0] = 0.0;
456 3 : adfGeoTransform[1] = 1.0;
457 3 : adfGeoTransform[2] = 0.0;
458 3 : adfGeoTransform[3] = 0.0;
459 3 : adfGeoTransform[4] = 0.0;
460 3 : adfGeoTransform[5] = 1.0;
461 3 : }
462 :
463 : /************************************************************************/
464 : /* ~IRISDataset() */
465 : /************************************************************************/
466 :
467 6 : IRISDataset::~IRISDataset()
468 :
469 : {
470 3 : FlushCache(true);
471 3 : if (fp != nullptr)
472 3 : VSIFCloseL(fp);
473 6 : }
474 :
475 : /************************************************************************/
476 : /* Calculates the projection and Geotransform */
477 : /************************************************************************/
478 1 : void IRISDataset::LoadProjection() const
479 : {
480 1 : bHasLoadedProjection = true;
481 : // They give the radius in cm.
482 1 : double dfEquatorialRadius =
483 1 : CPL_LSBUINT32PTR(abyHeader + 220 + 320 + 12) / 100.0;
484 : // Point 3.2.27 pag 3-15.
485 1 : double dfInvFlattening =
486 1 : CPL_LSBUINT32PTR(abyHeader + 224 + 320 + 12) / 1000000.0;
487 1 : double dfFlattening = 0.0;
488 1 : double dfPolarRadius = 0.0;
489 :
490 1 : if (dfEquatorialRadius == 0.0)
491 : {
492 : // If Radius is 0, change to 6371000 Point 3.2.27 pag 3-15 (old IRIS
493 : // versions).
494 0 : dfEquatorialRadius = 6371000.0;
495 0 : dfPolarRadius = dfEquatorialRadius;
496 0 : dfInvFlattening = 0.0;
497 0 : dfFlattening = 0.0;
498 : }
499 : else
500 : {
501 1 : if (dfInvFlattening == 0.0)
502 : {
503 : // When inverse flattening is infinite, they use 0.
504 1 : dfFlattening = 0.0;
505 1 : dfPolarRadius = dfEquatorialRadius;
506 : }
507 : else
508 : {
509 0 : dfFlattening = 1.0 / dfInvFlattening;
510 0 : dfPolarRadius = dfEquatorialRadius * (1.0 - dfFlattening);
511 : }
512 : }
513 :
514 1 : constexpr GUInt32 knUINT32_MAX = 0xFFFFFFFFU;
515 1 : const double dfCenterLon =
516 1 : CPL_LSBUINT32PTR(abyHeader + 112 + 320 + 12) * 360.0 / knUINT32_MAX;
517 1 : const double dfCenterLat =
518 1 : CPL_LSBUINT32PTR(abyHeader + 108 + 320 + 12) * 360.0 / knUINT32_MAX;
519 :
520 1 : const double dfProjRefLon =
521 1 : CPL_LSBUINT32PTR(abyHeader + 244 + 320 + 12) * 360.0 / knUINT32_MAX;
522 1 : const double dfProjRefLat =
523 1 : CPL_LSBUINT32PTR(abyHeader + 240 + 320 + 12) * 360.0 / knUINT32_MAX;
524 :
525 1 : const double dfRadarLocX = CPL_LSBSINT32PTR(abyHeader + 112 + 12) / 1000.0;
526 1 : const double dfRadarLocY = CPL_LSBSINT32PTR(abyHeader + 116 + 12) / 1000.0;
527 :
528 1 : const double dfScaleX = CPL_LSBSINT32PTR(abyHeader + 88 + 12) / 100.0;
529 1 : const double dfScaleY = CPL_LSBSINT32PTR(abyHeader + 92 + 12) / 100.0;
530 1 : if (dfScaleX <= 0.0 || dfScaleY <= 0.0 || dfScaleX >= dfPolarRadius ||
531 : dfScaleY >= dfPolarRadius)
532 0 : return;
533 :
534 : // Mercator projection.
535 1 : if (EQUAL(aszProjections[nProjectionCode], "Mercator"))
536 : {
537 1 : std::pair<double, double> oPositionX2;
538 1 : if (!GeodesicCalculation(dfCenterLat, dfCenterLon, 90.0, dfScaleX,
539 : dfEquatorialRadius, dfPolarRadius,
540 : dfFlattening, oPositionX2))
541 0 : return;
542 1 : std::pair<double, double> oPositionY2;
543 1 : if (!GeodesicCalculation(dfCenterLat, dfCenterLon, 0.0, dfScaleY,
544 : dfEquatorialRadius, dfPolarRadius,
545 : dfFlattening, oPositionY2))
546 0 : return;
547 :
548 1 : m_oSRS.SetGeogCS("unnamed ellipse", "unknown", "unnamed",
549 : dfEquatorialRadius, dfInvFlattening, "Greenwich", 0.0,
550 : "degree", 0.0174532925199433);
551 :
552 1 : m_oSRS.SetMercator(dfProjRefLat, dfProjRefLon, 1.0, 0.0, 0.0);
553 1 : m_oSRS.SetLinearUnits("Metre", 1.0);
554 :
555 : // The center coordinates are given in LatLon on the defined
556 : // ellipsoid. Necessary to calculate geotransform.
557 :
558 2 : OGRSpatialReference oSRSLatLon;
559 1 : oSRSLatLon.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
560 1 : oSRSLatLon.SetGeogCS("unnamed ellipse", "unknown", "unnamed",
561 : dfEquatorialRadius, dfInvFlattening, "Greenwich",
562 : 0.0, "degree", 0.0174532925199433);
563 :
564 : OGRCoordinateTransformation *poTransform =
565 1 : OGRCreateCoordinateTransformation(&oSRSLatLon, &m_oSRS);
566 :
567 1 : const double dfLon2 = oPositionX2.first;
568 1 : const double dfLat2 = oPositionY2.second;
569 :
570 1 : double dfX = dfCenterLon;
571 1 : double dfY = dfCenterLat;
572 1 : if (poTransform == nullptr || !poTransform->Transform(1, &dfX, &dfY))
573 0 : CPLError(CE_Failure, CPLE_None, "Transformation Failed");
574 :
575 1 : double dfX2 = dfLon2;
576 1 : double dfY2 = dfLat2;
577 1 : if (poTransform == nullptr || !poTransform->Transform(1, &dfX2, &dfY2))
578 0 : CPLError(CE_Failure, CPLE_None, "Transformation Failed");
579 :
580 1 : adfGeoTransform[0] = dfX - (dfRadarLocX * (dfX2 - dfX));
581 1 : adfGeoTransform[1] = dfX2 - dfX;
582 1 : adfGeoTransform[2] = 0.0;
583 1 : adfGeoTransform[3] = dfY + (dfRadarLocY * (dfY2 - dfY));
584 1 : adfGeoTransform[4] = 0.0;
585 1 : adfGeoTransform[5] = -1 * (dfY2 - dfY);
586 :
587 1 : delete poTransform;
588 : }
589 0 : else if (EQUAL(aszProjections[nProjectionCode], "Azimutal equidistant"))
590 : {
591 0 : m_oSRS.SetGeogCS("unnamed ellipse", "unknown", "unnamed",
592 : dfEquatorialRadius, dfInvFlattening, "Greenwich", 0.0,
593 : "degree", 0.0174532925199433);
594 0 : m_oSRS.SetAE(dfProjRefLat, dfProjRefLon, 0.0, 0.0);
595 :
596 0 : adfGeoTransform[0] = -1 * (dfRadarLocX * dfScaleX);
597 0 : adfGeoTransform[1] = dfScaleX;
598 0 : adfGeoTransform[2] = 0.0;
599 0 : adfGeoTransform[3] = dfRadarLocY * dfScaleY;
600 0 : adfGeoTransform[4] = 0.0;
601 0 : adfGeoTransform[5] = -1 * dfScaleY;
602 : // When the projection is different from Mercator or Azimutal
603 : // equidistant, we set a standard geotransform.
604 : }
605 : else
606 : {
607 0 : adfGeoTransform[0] = -1 * (dfRadarLocX * dfScaleX);
608 0 : adfGeoTransform[1] = dfScaleX;
609 0 : adfGeoTransform[2] = 0.0;
610 0 : adfGeoTransform[3] = dfRadarLocY * dfScaleY;
611 0 : adfGeoTransform[4] = 0.0;
612 0 : adfGeoTransform[5] = -1 * dfScaleY;
613 : }
614 : }
615 :
616 : /******************************************************************************/
617 : /* The geotransform in Mercator projection must be calculated transforming */
618 : /* distance to degrees over the ellipsoid, using Vincenty's formula. */
619 : /* The following method is ported from a version for Javascript by Chris */
620 : /* Veness distributed under a CC-BY 3.0 licence, whose conditions is that the */
621 : /* following copyright notice is retained as well as the link to : */
622 : /* http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html */
623 : /******************************************************************************/
624 :
625 : /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
626 : * - - - - - - - - */
627 : /* Vincenty Direct Solution of Geodesics on the Ellipsoid (c) Chris Veness
628 : * 2005-2012 */
629 : /* */
630 : /* from: Vincenty direct formula - T Vincenty, "Direct and Inverse Solutions of
631 : * Geodesics on the */
632 : /* Ellipsoid with application of nested equations", Survey Review, vol
633 : * XXII no 176, 1975 */
634 : /* http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf */
635 : /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
636 : * - - - - - - - - */
637 :
638 2 : bool IRISDataset::GeodesicCalculation(double fLat, double fLon, double fAngle,
639 : double fDist, double fEquatorialRadius,
640 : double fPolarRadius, double fFlattening,
641 : std::pair<double, double> &oOutPair)
642 : {
643 2 : const double dfAlpha1 = DEG2RAD * fAngle;
644 2 : const double dfSinAlpha1 = sin(dfAlpha1);
645 2 : const double dfCosAlpha1 = cos(dfAlpha1);
646 :
647 2 : const double dfTanU1 = (1 - fFlattening) * tan(fLat * DEG2RAD);
648 2 : const double dfCosU1 = 1 / sqrt((1 + dfTanU1 * dfTanU1));
649 2 : const double dfSinU1 = dfTanU1 * dfCosU1;
650 :
651 2 : const double dfSigma1 = atan2(dfTanU1, dfCosAlpha1);
652 2 : const double dfSinAlpha = dfCosU1 * dfSinAlpha1;
653 2 : const double dfCosSqAlpha = 1 - dfSinAlpha * dfSinAlpha;
654 2 : const double dfUSq =
655 2 : dfCosSqAlpha *
656 2 : (fEquatorialRadius * fEquatorialRadius - fPolarRadius * fPolarRadius) /
657 2 : (fPolarRadius * fPolarRadius);
658 2 : const double dfA =
659 : 1 +
660 2 : dfUSq / 16384 * (4096 + dfUSq * (-768 + dfUSq * (320 - 175 * dfUSq)));
661 2 : const double dfB =
662 2 : dfUSq / 1024 * (256 + dfUSq * (-128 + dfUSq * (74 - 47 * dfUSq)));
663 :
664 2 : double dfSigma = fDist / (fPolarRadius * dfA);
665 2 : double dfSigmaP = 2 * M_PI;
666 :
667 2 : double dfSinSigma = 0.0;
668 2 : double dfCosSigma = 0.0;
669 2 : double dfCos2SigmaM = 0.0;
670 :
671 2 : int nIter = 0;
672 4 : while (fabs(dfSigma - dfSigmaP) > 1e-12)
673 : {
674 2 : dfCos2SigmaM = cos(2 * dfSigma1 + dfSigma);
675 2 : dfSinSigma = sin(dfSigma);
676 2 : dfCosSigma = cos(dfSigma);
677 2 : const double dfDeltaSigma =
678 2 : dfB * dfSinSigma *
679 2 : (dfCos2SigmaM +
680 2 : dfB / 4 *
681 2 : (dfCosSigma * (-1 + 2 * dfCos2SigmaM * dfCos2SigmaM) -
682 2 : dfB / 6 * dfCos2SigmaM * (-3 + 4 * dfSinSigma * dfSinSigma) *
683 2 : (-3 + 4 * dfCos2SigmaM * dfCos2SigmaM)));
684 2 : dfSigmaP = dfSigma;
685 2 : dfSigma = fDist / (fPolarRadius * dfA) + dfDeltaSigma;
686 2 : nIter++;
687 2 : if (nIter == 100)
688 0 : return false;
689 : }
690 :
691 2 : const double dfTmp =
692 2 : dfSinU1 * dfSinSigma - dfCosU1 * dfCosSigma * dfCosAlpha1;
693 2 : const double dfLat2 = atan2(
694 2 : dfSinU1 * dfCosSigma + dfCosU1 * dfSinSigma * dfCosAlpha1,
695 2 : (1 - fFlattening) * sqrt(dfSinAlpha * dfSinAlpha + dfTmp * dfTmp));
696 : const double dfLambda =
697 2 : atan2(dfSinSigma * dfSinAlpha1,
698 2 : dfCosU1 * dfCosSigma - dfSinU1 * dfSinSigma * dfCosAlpha1);
699 2 : const double dfC = fFlattening / 16 * dfCosSqAlpha *
700 2 : (4 + fFlattening * (4 - 3 * dfCosSqAlpha));
701 2 : const double dfL =
702 : dfLambda -
703 2 : (1 - dfC) * fFlattening * dfSinAlpha *
704 2 : (dfSigma +
705 2 : dfC * dfSinSigma *
706 2 : (dfCos2SigmaM +
707 2 : dfC * dfCosSigma * (-1 + 2 * dfCos2SigmaM * dfCos2SigmaM)));
708 2 : double dfLon2 = fLon * DEG2RAD + dfL;
709 :
710 2 : if (dfLon2 > M_PI)
711 0 : dfLon2 = dfLon2 - 2 * M_PI;
712 2 : if (dfLon2 < -1 * M_PI)
713 0 : dfLon2 = dfLon2 + 2 * M_PI;
714 :
715 2 : oOutPair = std::pair<double, double>(dfLon2 * RAD2DEG, dfLat2 * RAD2DEG);
716 :
717 2 : return true;
718 : }
719 :
720 : /************************************************************************/
721 : /* GetGeoTransform() */
722 : /************************************************************************/
723 :
724 1 : CPLErr IRISDataset::GetGeoTransform(double *padfTransform)
725 :
726 : {
727 1 : if (!bHasLoadedProjection)
728 0 : LoadProjection();
729 1 : memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
730 1 : return CE_None;
731 : }
732 :
733 : /************************************************************************/
734 : /* GetSpatialRef() */
735 : /************************************************************************/
736 :
737 1 : const OGRSpatialReference *IRISDataset::GetSpatialRef() const
738 : {
739 1 : if (!bHasLoadedProjection)
740 1 : LoadProjection();
741 1 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
742 : }
743 :
744 : /************************************************************************/
745 : /* Identify() */
746 : /************************************************************************/
747 :
748 47994 : int IRISDataset::Identify(GDALOpenInfo *poOpenInfo)
749 :
750 : {
751 : /* -------------------------------------------------------------------- */
752 : /* Confirm that the file is an IRIS file */
753 : /* -------------------------------------------------------------------- */
754 47994 : if (poOpenInfo->nHeaderBytes < 640)
755 46218 : return FALSE;
756 :
757 1776 : const short nId1 = CPL_LSBSINT16PTR(poOpenInfo->pabyHeader);
758 1776 : const short nId2 = CPL_LSBSINT16PTR(poOpenInfo->pabyHeader + 12);
759 1776 : unsigned short nProductCode =
760 1776 : CPL_LSBUINT16PTR(poOpenInfo->pabyHeader + 12 + 12);
761 1776 : const short nYear = CPL_LSBSINT16PTR(poOpenInfo->pabyHeader + 26 + 12);
762 1776 : const short nMonth = CPL_LSBSINT16PTR(poOpenInfo->pabyHeader + 28 + 12);
763 1776 : const short nDay = CPL_LSBSINT16PTR(poOpenInfo->pabyHeader + 30 + 12);
764 :
765 : // Check if the two headers are 27 (product hdr) & 26 (product
766 : // configuration), and other metadata
767 1782 : if (!(nId1 == 27 && nId2 == 26 && nProductCode > 0 &&
768 6 : nProductCode < CPL_ARRAYSIZE(aszProductNames) && nYear >= 1900 &&
769 6 : nYear < 2100 && nMonth >= 1 && nMonth <= 12 && nDay >= 1 &&
770 : nDay <= 31))
771 1770 : return FALSE;
772 :
773 6 : return TRUE;
774 : }
775 :
776 : /************************************************************************/
777 : /* FillString() */
778 : /************************************************************************/
779 :
780 21 : static void FillString(char *szBuffer, size_t nBufferSize, void *pSrcBuffer)
781 : {
782 285 : for (size_t i = 0; i < nBufferSize - 1; i++)
783 264 : szBuffer[i] = static_cast<char *>(pSrcBuffer)[i];
784 21 : szBuffer[nBufferSize - 1] = '\0';
785 21 : }
786 :
787 : /************************************************************************/
788 : /* Open() */
789 : /************************************************************************/
790 :
791 3 : GDALDataset *IRISDataset::Open(GDALOpenInfo *poOpenInfo)
792 :
793 : {
794 3 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
795 0 : return nullptr;
796 : /* -------------------------------------------------------------------- */
797 : /* Confirm the requested access is supported. */
798 : /* -------------------------------------------------------------------- */
799 3 : if (poOpenInfo->eAccess == GA_Update)
800 : {
801 0 : CPLError(CE_Failure, CPLE_NotSupported,
802 : "The IRIS driver does not support update access to existing"
803 : " datasets.");
804 0 : return nullptr;
805 : }
806 :
807 : /* -------------------------------------------------------------------- */
808 : /* Create a corresponding GDALDataset. */
809 : /* -------------------------------------------------------------------- */
810 3 : IRISDataset *poDS = new IRISDataset();
811 3 : poDS->fp = poOpenInfo->fpL;
812 3 : poOpenInfo->fpL = nullptr;
813 :
814 : /* -------------------------------------------------------------------- */
815 : /* Read the header. */
816 : /* -------------------------------------------------------------------- */
817 3 : VSIFReadL(poDS->abyHeader, 1, 640, poDS->fp);
818 3 : const int nXSize = CPL_LSBSINT32PTR(poDS->abyHeader + 100 + 12);
819 3 : const int nYSize = CPL_LSBSINT32PTR(poDS->abyHeader + 104 + 12);
820 3 : const int nNumBands = CPL_LSBSINT32PTR(poDS->abyHeader + 108 + 12);
821 :
822 3 : poDS->nRasterXSize = nXSize;
823 :
824 3 : poDS->nRasterYSize = nYSize;
825 3 : if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
826 : {
827 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions : %d x %d",
828 : poDS->nRasterXSize, poDS->nRasterYSize);
829 0 : delete poDS;
830 0 : return nullptr;
831 : }
832 :
833 3 : if (!GDALCheckBandCount(nNumBands, TRUE))
834 : {
835 0 : delete poDS;
836 0 : return nullptr;
837 : }
838 :
839 : /* -------------------------------------------------------------------- */
840 : /* Setting the Metadata */
841 : /* -------------------------------------------------------------------- */
842 : // See point 3.2.26 at page 3.12 of the manual.
843 3 : poDS->nProductCode = CPL_LSBUINT16PTR(poDS->abyHeader + 12 + 12);
844 3 : poDS->SetMetadataItem("PRODUCT_ID",
845 6 : CPLString().Printf("%d", poDS->nProductCode));
846 3 : if (poDS->nProductCode >= CPL_ARRAYSIZE(aszProductNames))
847 : {
848 0 : delete poDS;
849 0 : return nullptr;
850 : }
851 :
852 3 : poDS->SetMetadataItem("PRODUCT", aszProductNames[poDS->nProductCode]);
853 :
854 3 : poDS->nDataTypeCode = CPL_LSBUINT16PTR(poDS->abyHeader + 130 + 12);
855 3 : if (poDS->nDataTypeCode >= CPL_ARRAYSIZE(aszDataTypeCodes))
856 : {
857 0 : delete poDS;
858 0 : return nullptr;
859 : }
860 3 : poDS->SetMetadataItem("DATA_TYPE_CODE",
861 3 : aszDataTypeCodes[poDS->nDataTypeCode]);
862 :
863 3 : if (poDS->nDataTypeCode >= CPL_ARRAYSIZE(aszDataTypes))
864 : {
865 0 : delete poDS;
866 0 : return nullptr;
867 : }
868 3 : poDS->SetMetadataItem("DATA_TYPE", aszDataTypes[poDS->nDataTypeCode]);
869 :
870 3 : const unsigned short nDataTypeInputCode =
871 3 : CPL_LSBUINT16PTR(poDS->abyHeader + 144 + 12);
872 3 : if (nDataTypeInputCode >= CPL_ARRAYSIZE(aszDataTypeCodes))
873 : {
874 0 : delete poDS;
875 0 : return nullptr;
876 : }
877 3 : poDS->SetMetadataItem("DATA_TYPE_INPUT_CODE",
878 3 : aszDataTypeCodes[nDataTypeInputCode]);
879 :
880 3 : const unsigned short nDataTypeInput =
881 3 : CPL_LSBUINT16PTR(poDS->abyHeader + 144 + 12);
882 3 : if (nDataTypeInput >= CPL_ARRAYSIZE(aszDataTypes))
883 : {
884 0 : delete poDS;
885 0 : return nullptr;
886 : }
887 3 : poDS->SetMetadataItem("DATA_TYPE_INPUT", aszDataTypes[nDataTypeInput]);
888 :
889 3 : poDS->nProjectionCode =
890 3 : *static_cast<unsigned char *>(poDS->abyHeader + 146 + 12);
891 3 : if (poDS->nProjectionCode >= CPL_ARRAYSIZE(aszProjections))
892 : {
893 0 : delete poDS;
894 0 : return nullptr;
895 : }
896 :
897 : // Times.
898 : {
899 3 : const int nSeconds = CPL_LSBSINT32PTR(poDS->abyHeader + 20 + 12);
900 :
901 3 : const int nHour = (nSeconds - (nSeconds % 3600)) / 3600;
902 3 : const int nMinute =
903 3 : ((nSeconds - nHour * 3600) - (nSeconds - nHour * 3600) % 60) / 60;
904 3 : const int nSecond = nSeconds - nHour * 3600 - nMinute * 60;
905 :
906 3 : const short nYear = CPL_LSBSINT16PTR(poDS->abyHeader + 26 + 12);
907 3 : const short nMonth = CPL_LSBSINT16PTR(poDS->abyHeader + 28 + 12);
908 3 : const short nDay = CPL_LSBSINT16PTR(poDS->abyHeader + 30 + 12);
909 :
910 3 : poDS->SetMetadataItem("TIME_PRODUCT_GENERATED",
911 3 : CPLString().Printf("%d-%02d-%02d %02d:%02d:%02d",
912 : nYear, nMonth, nDay, nHour,
913 3 : nMinute, nSecond));
914 : }
915 :
916 : {
917 3 : const int nSeconds = CPL_LSBSINT32PTR(poDS->abyHeader + 32 + 12);
918 :
919 3 : const int nHour = (nSeconds - (nSeconds % 3600)) / 3600;
920 3 : const int nMinute =
921 3 : ((nSeconds - nHour * 3600) - (nSeconds - nHour * 3600) % 60) / 60;
922 3 : const int nSecond = nSeconds - nHour * 3600 - nMinute * 60;
923 :
924 3 : const short nYear = CPL_LSBSINT16PTR(poDS->abyHeader + 26 + 12);
925 3 : const short nMonth = CPL_LSBSINT16PTR(poDS->abyHeader + 28 + 12);
926 3 : const short nDay = CPL_LSBSINT16PTR(poDS->abyHeader + 30 + 12);
927 :
928 3 : poDS->SetMetadataItem("TIME_INPUT_INGEST_SWEEP",
929 3 : CPLString().Printf("%d-%02d-%02d %02d:%02d:%02d",
930 : nYear, nMonth, nDay, nHour,
931 3 : nMinute, nSecond));
932 : }
933 :
934 : // Site and task information.
935 :
936 3 : char szSiteName[17] = {}; // Must have one extra char for string end.
937 3 : char szVersionName[9] = {};
938 :
939 3 : FillString(szSiteName, sizeof(szSiteName), poDS->abyHeader + 320 + 12);
940 3 : FillString(szVersionName, sizeof(szVersionName),
941 3 : poDS->abyHeader + 16 + 320 + 12);
942 3 : poDS->SetMetadataItem("PRODUCT_SITE_NAME", szSiteName);
943 3 : poDS->SetMetadataItem("PRODUCT_SITE_IRIS_VERSION", szVersionName);
944 :
945 3 : FillString(szSiteName, sizeof(szSiteName), poDS->abyHeader + 90 + 320 + 12);
946 3 : FillString(szVersionName, sizeof(szVersionName),
947 3 : poDS->abyHeader + 24 + 320 + 12);
948 3 : poDS->SetMetadataItem("INGEST_SITE_NAME", szSiteName);
949 3 : poDS->SetMetadataItem("INGEST_SITE_IRIS_VERSION", szVersionName);
950 :
951 3 : FillString(szSiteName, sizeof(szSiteName), poDS->abyHeader + 74 + 320 + 12);
952 3 : poDS->SetMetadataItem("INGEST_HARDWARE_NAME", szSiteName);
953 :
954 3 : char szConfigFile[13] = {};
955 3 : FillString(szConfigFile, sizeof(szConfigFile), poDS->abyHeader + 62 + 12);
956 3 : poDS->SetMetadataItem("PRODUCT_CONFIGURATION_NAME", szConfigFile);
957 :
958 3 : char szTaskName[13] = {};
959 3 : FillString(szTaskName, sizeof(szTaskName), poDS->abyHeader + 74 + 12);
960 3 : poDS->SetMetadataItem("TASK_NAME", szTaskName);
961 :
962 3 : const short nRadarHeight =
963 3 : CPL_LSBSINT16PTR(poDS->abyHeader + 284 + 320 + 12);
964 3 : poDS->SetMetadataItem("RADAR_HEIGHT",
965 6 : CPLString().Printf("%d m", nRadarHeight));
966 : // Ground height over the sea level.
967 3 : const short nGroundHeight =
968 3 : CPL_LSBSINT16PTR(poDS->abyHeader + 118 + 320 + 12);
969 3 : poDS->SetMetadataItem(
970 : "GROUND_HEIGHT",
971 6 : CPLString().Printf("%d m", nRadarHeight - nGroundHeight));
972 :
973 3 : unsigned short nFlags = CPL_LSBUINT16PTR(poDS->abyHeader + 86 + 12);
974 : // Get eleventh bit.
975 3 : nFlags = nFlags << 4;
976 3 : nFlags = nFlags >> 15;
977 3 : if (nFlags == 1)
978 : {
979 1 : poDS->SetMetadataItem("COMPOSITED_PRODUCT", "YES");
980 1 : const unsigned int compositedMask =
981 1 : CPL_LSBUINT32PTR(poDS->abyHeader + 232 + 320 + 12);
982 1 : poDS->SetMetadataItem("COMPOSITED_PRODUCT_MASK",
983 2 : CPLString().Printf("0x%08x", compositedMask));
984 : }
985 : else
986 : {
987 2 : poDS->SetMetadataItem("COMPOSITED_PRODUCT", "NO");
988 : }
989 :
990 : // Wave values.
991 3 : poDS->SetMetadataItem(
992 3 : "PRF", CPLString().Printf("%d Hz", CPL_LSBSINT32PTR(poDS->abyHeader +
993 3 : 120 + 320 + 12)));
994 3 : poDS->SetMetadataItem(
995 : "WAVELENGTH",
996 3 : CPLString().Printf("%4.2f cm",
997 3 : CPL_LSBSINT32PTR(poDS->abyHeader + 148 + 320 + 12) /
998 3 : 100.0f));
999 3 : const unsigned short nPolarizationType =
1000 3 : CPL_LSBUINT16PTR(poDS->abyHeader + 172 + 320 + 12);
1001 :
1002 : // See section 3.3.37 & 3.2.54.
1003 3 : float fNyquist = (CPL_LSBSINT32PTR(poDS->abyHeader + 120 + 320 + 12)) *
1004 3 : (static_cast<float>(
1005 3 : CPL_LSBSINT32PTR(poDS->abyHeader + 148 + 320 + 12)) /
1006 : 10000.0f) /
1007 : 4.0f;
1008 3 : if (nPolarizationType == 1)
1009 0 : fNyquist = fNyquist * 2.0f;
1010 3 : else if (nPolarizationType == 2)
1011 0 : fNyquist = fNyquist * 3.0f;
1012 3 : else if (nPolarizationType == 3)
1013 0 : fNyquist = fNyquist * 4.0f;
1014 3 : poDS->fNyquistVelocity = fNyquist;
1015 3 : poDS->SetMetadataItem("NYQUIST_VELOCITY",
1016 6 : CPLString().Printf("%.2f m/s", fNyquist));
1017 :
1018 : // Product dependent metadata (stored in 80 bytes from 162 bytes
1019 : // at the product header) See point 3.2.30 at page 3.19 of the
1020 : // manual.
1021 : // See point 3.2.25 at page 3.12 of the manual.
1022 3 : if (EQUAL(aszProductNames[poDS->nProductCode], "PPI"))
1023 : {
1024 : // Degrees = 360 * (Binary Angle)*2^N
1025 2 : const float fElevation =
1026 2 : CPL_LSBSINT16PTR(poDS->abyHeader + 164 + 12) * 360.0f / 65536.0f;
1027 :
1028 2 : poDS->SetMetadataItem("PPI_ELEVATION_ANGLE",
1029 4 : CPLString().Printf("%f", fElevation));
1030 2 : if (EQUAL(aszDataTypeCodes[poDS->nDataTypeCode], "dBZ"))
1031 2 : poDS->SetMetadataItem("DATA_TYPE_UNITS", "dBZ");
1032 : else
1033 0 : poDS->SetMetadataItem("DATA_TYPE_UNITS", "m/s");
1034 : // See point 3.2.2 at page 3.2 of the manual.
1035 : }
1036 1 : else if (EQUAL(aszProductNames[poDS->nProductCode], "CAPPI"))
1037 : {
1038 1 : const float fElevation =
1039 1 : CPL_LSBSINT32PTR(poDS->abyHeader + 4 + 164 + 12) / 100.0f;
1040 1 : poDS->SetMetadataItem("CAPPI_BOTTOM_HEIGHT",
1041 2 : CPLString().Printf("%.1f m", fElevation));
1042 1 : const float fAzimuthSmoothingForShear =
1043 1 : CPL_LSBUINT16PTR(poDS->abyHeader + 10 + 164 + 12) * 360.0f /
1044 : 65536.0f;
1045 1 : poDS->SetMetadataItem(
1046 : "AZIMUTH_SMOOTHING_FOR_SHEAR",
1047 2 : CPLString().Printf("%.1f", fAzimuthSmoothingForShear));
1048 1 : const unsigned int nMaxAgeVVPCorrection =
1049 1 : CPL_LSBUINT32PTR(poDS->abyHeader + 24 + 164 + 12);
1050 1 : poDS->SetMetadataItem("MAX_AGE_FOR_SHEAR_VVP_CORRECTION",
1051 2 : CPLString().Printf("%d s", nMaxAgeVVPCorrection));
1052 1 : if (EQUAL(aszDataTypeCodes[poDS->nDataTypeCode], "dBZ"))
1053 1 : poDS->SetMetadataItem("DATA_TYPE_UNITS", "dBZ");
1054 : else
1055 0 : poDS->SetMetadataItem("DATA_TYPE_UNITS", "m/s");
1056 : // See point 3.2.32 at page 3.19 of the manual.
1057 : }
1058 0 : else if (EQUAL(aszProductNames[poDS->nProductCode], "RAIN1") ||
1059 0 : EQUAL(aszProductNames[poDS->nProductCode], "RAINN"))
1060 : {
1061 0 : const short nNumProducts =
1062 0 : CPL_LSBSINT16PTR(poDS->abyHeader + 170 + 320 + 12);
1063 0 : poDS->SetMetadataItem("NUM_FILES_USED",
1064 0 : CPLString().Printf("%d", nNumProducts));
1065 :
1066 0 : const float fMinZAcum =
1067 0 : (CPL_LSBUINT32PTR(poDS->abyHeader + 164 + 12) - 32768.0f) /
1068 : 10000.0f;
1069 0 : poDS->SetMetadataItem("MINIMUM_Z_TO_ACCUMULATE",
1070 0 : CPLString().Printf("%f", fMinZAcum));
1071 :
1072 0 : const unsigned short nSecondsOfAccumulation =
1073 0 : CPL_LSBUINT16PTR(poDS->abyHeader + 6 + 164 + 12);
1074 0 : poDS->SetMetadataItem(
1075 : "SECONDS_OF_ACCUMULATION",
1076 0 : CPLString().Printf("%d s", nSecondsOfAccumulation));
1077 :
1078 0 : const unsigned int nSpanInputFiles =
1079 0 : CPL_LSBUINT32PTR(poDS->abyHeader + 24 + 164 + 12);
1080 0 : poDS->SetMetadataItem("SPAN_OF_INPUT_FILES",
1081 0 : CPLString().Printf("%d s", nSpanInputFiles));
1082 0 : poDS->SetMetadataItem("DATA_TYPE_UNITS", "mm");
1083 :
1084 0 : char szInputProductName[13] = "";
1085 0 : for (int k = 0; k < 12; k++)
1086 0 : szInputProductName[k] =
1087 0 : *reinterpret_cast<char *>(poDS->abyHeader + k + 12 + 164 + 12);
1088 :
1089 0 : poDS->SetMetadataItem("INPUT_PRODUCT_NAME",
1090 0 : CPLString().Printf("%s", szInputProductName));
1091 :
1092 0 : if (EQUAL(aszProductNames[poDS->nProductCode], "RAINN"))
1093 0 : poDS->SetMetadataItem(
1094 : "NUM_HOURS_ACCUMULATE",
1095 0 : CPLString().Printf(
1096 0 : "%d", CPL_LSBUINT16PTR(poDS->abyHeader + 10 + 164 + 12)));
1097 :
1098 : // See point 3.2.73 at page 3.36 of the manual.
1099 : }
1100 0 : else if (EQUAL(aszProductNames[poDS->nProductCode], "VIL"))
1101 : {
1102 0 : const float fBottomHeightInterval =
1103 0 : CPL_LSBSINT32PTR(poDS->abyHeader + 4 + 164 + 12) / 100.0f;
1104 : // TYPO in metadata key: FIXME ?
1105 0 : poDS->SetMetadataItem(
1106 : "BOTTOM_OF_HEIGTH_INTERVAL",
1107 0 : CPLString().Printf("%.1f m", fBottomHeightInterval));
1108 0 : const float fTopHeightInterval =
1109 0 : CPL_LSBSINT32PTR(poDS->abyHeader + 8 + 164 + 12) / 100.0f;
1110 : // TYPO in metadata key: FIXME ?
1111 0 : poDS->SetMetadataItem("TOP_OF_HEIGTH_INTERVAL",
1112 0 : CPLString().Printf("%.1f m", fTopHeightInterval));
1113 0 : poDS->SetMetadataItem("VIL_DENSITY_NOT_AVAILABLE_VALUE", "-1");
1114 0 : poDS->SetMetadataItem("DATA_TYPE_UNITS", "mm");
1115 : // See point 3.2.68 at page 3.36 of the manual
1116 : }
1117 0 : else if (EQUAL(aszProductNames[poDS->nProductCode], "TOPS"))
1118 : {
1119 0 : const float fZThreshold =
1120 0 : CPL_LSBSINT16PTR(poDS->abyHeader + 4 + 164 + 12) / 16.0f;
1121 0 : poDS->SetMetadataItem("Z_THRESHOLD",
1122 0 : CPLString().Printf("%.1f dBZ", fZThreshold));
1123 0 : poDS->SetMetadataItem("ECHO_TOPS_NOT_AVAILABLE_VALUE", "-1");
1124 0 : poDS->SetMetadataItem("DATA_TYPE_UNITS", "km");
1125 : // See point 3.2.20 at page 3.10 of the manual.
1126 : }
1127 0 : else if (EQUAL(aszProductNames[poDS->nProductCode], "MAX"))
1128 : {
1129 0 : const float fBottomInterval =
1130 0 : CPL_LSBSINT32PTR(poDS->abyHeader + 4 + 164 + 12) / 100.0f;
1131 0 : poDS->SetMetadataItem("BOTTOM_OF_INTERVAL",
1132 0 : CPLString().Printf("%.1f m", fBottomInterval));
1133 0 : const float fTopInterval =
1134 0 : CPL_LSBSINT32PTR(poDS->abyHeader + 8 + 164 + 12) / 100.0f;
1135 0 : poDS->SetMetadataItem("TOP_OF_INTERVAL",
1136 0 : CPLString().Printf("%.1f m", fTopInterval));
1137 0 : const int nNumPixelsSidePanels =
1138 0 : CPL_LSBSINT32PTR(poDS->abyHeader + 12 + 164 + 12);
1139 0 : poDS->SetMetadataItem("NUM_PIXELS_SIDE_PANELS",
1140 0 : CPLString().Printf("%d", nNumPixelsSidePanels));
1141 0 : const short nHorizontalSmootherSidePanels =
1142 0 : CPL_LSBSINT16PTR(poDS->abyHeader + 16 + 164 + 12);
1143 0 : poDS->SetMetadataItem(
1144 : "HORIZONTAL_SMOOTHER_SIDE_PANELS",
1145 0 : CPLString().Printf("%d", nHorizontalSmootherSidePanels));
1146 0 : const short nVerticalSmootherSidePanels =
1147 0 : CPL_LSBSINT16PTR(poDS->abyHeader + 18 + 164 + 12);
1148 0 : poDS->SetMetadataItem(
1149 : "VERTICAL_SMOOTHER_SIDE_PANELS",
1150 0 : CPLString().Printf("%d", nVerticalSmootherSidePanels));
1151 : }
1152 :
1153 : /* -------------------------------------------------------------------- */
1154 : /* Create band information objects. */
1155 : /* -------------------------------------------------------------------- */
1156 : // coverity[tainted_data]
1157 6 : for (int iBandNum = 1; iBandNum <= nNumBands; iBandNum++)
1158 : {
1159 3 : poDS->SetBand(iBandNum, new IRISRasterBand(poDS, iBandNum));
1160 :
1161 3 : poDS->GetRasterBand(iBandNum)->SetNoDataValue(-9999);
1162 : // Calculating the band height to include it in the band metadata. Only
1163 : // for the CAPPI product.
1164 3 : if (EQUAL(aszProductNames[poDS->nProductCode], "CAPPI"))
1165 : {
1166 1 : const float fScaleZ =
1167 1 : CPL_LSBSINT32PTR(poDS->abyHeader + 96 + 12) / 100.0f;
1168 1 : const float fOffset =
1169 1 : CPL_LSBSINT32PTR(poDS->abyHeader + 4 + 164 + 12) / 100.0f;
1170 :
1171 2 : poDS->GetRasterBand(iBandNum)->SetMetadataItem(
1172 1 : "height", CPLString().Printf(
1173 1 : "%.0f m", fOffset + fScaleZ * (iBandNum - 1)));
1174 : }
1175 : }
1176 : /* -------------------------------------------------------------------- */
1177 : /* Initialize any PAM information. */
1178 : /* -------------------------------------------------------------------- */
1179 3 : poDS->SetDescription(poOpenInfo->pszFilename);
1180 3 : poDS->TryLoadXML();
1181 :
1182 : /* -------------------------------------------------------------------- */
1183 : /* Check for overviews. */
1184 : /* -------------------------------------------------------------------- */
1185 3 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
1186 :
1187 3 : return poDS;
1188 : }
1189 :
1190 : /************************************************************************/
1191 : /* GDALRegister_IRIS() */
1192 : /************************************************************************/
1193 :
1194 1520 : void GDALRegister_IRIS()
1195 :
1196 : {
1197 1520 : if (GDALGetDriverByName("IRIS") != nullptr)
1198 301 : return;
1199 :
1200 1219 : GDALDriver *poDriver = new GDALDriver();
1201 :
1202 1219 : poDriver->SetDescription("IRIS");
1203 1219 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1204 1219 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1205 1219 : "IRIS data (.PPI, .CAPPi etc)");
1206 1219 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/iris.html");
1207 1219 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "ppi");
1208 1219 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1209 :
1210 1219 : poDriver->pfnOpen = IRISDataset::Open;
1211 1219 : poDriver->pfnIdentify = IRISDataset::Identify;
1212 :
1213 1219 : GetGDALDriverManager()->RegisterDriver(poDriver);
1214 : }
|