Coverage Report

Created: 2026-09-26 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/airsar/airsardataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  AirSAR Reader
4
 * Purpose:  Implements read support for AirSAR Polarimetric data.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2007-2009, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_conv.h"
15
#include "cpl_string.h"
16
#include "cpl_vsi.h"
17
#include "gdal_frmts.h"
18
#include "gdal_pam.h"
19
#include "gdal_driver.h"
20
#include "gdal_drivermanager.h"
21
#include "gdal_openinfo.h"
22
#include "gdal_cpp_functions.h"
23
24
/************************************************************************/
25
/* ==================================================================== */
26
/*                              AirSARDataset                           */
27
/* ==================================================================== */
28
/************************************************************************/
29
30
class AirSARRasterBand;
31
32
class AirSARDataset final : public GDALPamDataset
33
{
34
    friend class AirSARRasterBand;
35
36
    VSILFILE *fp;
37
38
    int nLoadedLine;
39
    GByte *pabyCompressedLine;
40
    double *padfMatrix;
41
42
    int nDataStart;
43
    int nRecordLength;
44
45
    CPLErr LoadLine(int iLine);
46
47
    static char **ReadHeader(VSILFILE *fp, vsi_l_offset nFileOffset,
48
                             const char *pszPrefix, int nMaxLines);
49
50
  public:
51
    AirSARDataset();
52
    ~AirSARDataset() override;
53
54
    static GDALDataset *Open(GDALOpenInfo *);
55
};
56
57
/************************************************************************/
58
/* ==================================================================== */
59
/*                            AirSARRasterBand                          */
60
/* ==================================================================== */
61
/************************************************************************/
62
63
class AirSARRasterBand final : public GDALPamRasterBand
64
{
65
  public:
66
    AirSARRasterBand(AirSARDataset *, int);
67
    ~AirSARRasterBand() override;
68
69
    CPLErr IReadBlock(int, int, void *) override;
70
};
71
72
/* locations of stokes matrix values within padfMatrix ... same order as they
73
   are computed in the document. */
74
75
constexpr int M11 = 0;
76
constexpr int M12 = 1;
77
constexpr int M13 = 2;
78
constexpr int M14 = 3;
79
constexpr int M23 = 4;
80
constexpr int M24 = 5;
81
constexpr int M33 = 6;
82
constexpr int M34 = 7;
83
constexpr int M44 = 8;
84
constexpr int M22 = 9;
85
86
/************************************************************************/
87
/*                          AirSARRasterBand()                          */
88
/************************************************************************/
89
90
AirSARRasterBand::AirSARRasterBand(AirSARDataset *poDSIn, int nBandIn)
91
92
0
{
93
0
    poDS = poDSIn;
94
0
    nBand = nBandIn;
95
96
0
    nBlockXSize = poDS->GetRasterXSize();
97
0
    nBlockYSize = 1;
98
99
0
    if (this->nBand == 2 || this->nBand == 3 || this->nBand == 5)
100
0
        eDataType = GDT_CFloat32;
101
0
    else
102
0
        eDataType = GDT_Float32;
103
104
0
    switch (nBand)
105
0
    {
106
0
        case 1:
107
0
            SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_11");
108
0
            SetDescription("Covariance_11");
109
0
            eDataType = GDT_CFloat32;
110
0
            break;
111
112
0
        case 2:
113
0
            SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_12");
114
0
            SetDescription("Covariance_12");
115
0
            eDataType = GDT_CFloat32;
116
0
            break;
117
118
0
        case 3:
119
0
            SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_13");
120
0
            SetDescription("Covariance_13");
121
0
            eDataType = GDT_CFloat32;
122
0
            break;
123
124
0
        case 4:
125
0
            SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_22");
126
0
            SetDescription("Covariance_22");
127
0
            eDataType = GDT_CFloat32;
128
0
            break;
129
130
0
        case 5:
131
0
            SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_23");
132
0
            SetDescription("Covariance_23");
133
0
            eDataType = GDT_CFloat32;
134
0
            break;
135
136
0
        case 6:
137
0
            SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_33");
138
0
            SetDescription("Covariance_33");
139
0
            eDataType = GDT_CFloat32;
140
0
            break;
141
0
    }
142
0
}
143
144
/************************************************************************/
145
/*                         ~AirSARRasterBand()                          */
146
/************************************************************************/
147
148
AirSARRasterBand::~AirSARRasterBand()
149
150
0
{
151
0
}
152
153
/************************************************************************/
154
/*                             IReadBlock()                             */
155
/************************************************************************/
156
157
CPLErr AirSARRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
158
                                    void *pImage)
159
0
{
160
0
    float *pafLine = (float *)pImage;
161
0
    const double SQRT_2 = 1.4142135623730951;
162
163
0
    CPLErr eErr = cpl::down_cast<AirSARDataset *>(poDS)->LoadLine(nBlockYOff);
164
0
    if (eErr != CE_None)
165
0
        return eErr;
166
167
0
    double *padfMatrix = cpl::down_cast<AirSARDataset *>(poDS)->padfMatrix;
168
169
0
    if (nBand == 1) /* C11 */
170
0
    {
171
0
        for (int iPixel = 0; iPixel < nRasterXSize; iPixel++)
172
0
        {
173
0
            double *m = padfMatrix + 10 * iPixel;
174
175
0
            pafLine[iPixel * 2 + 0] = (float)(m[M11] + m[M22] + 2 * m[M12]);
176
0
            pafLine[iPixel * 2 + 1] = 0.0;
177
0
        }
178
0
    }
179
0
    else if (nBand == 2) /* C12 */
180
0
    {
181
0
        for (int iPixel = 0; iPixel < nRasterXSize; iPixel++)
182
0
        {
183
0
            double *m = padfMatrix + 10 * iPixel;
184
185
            // real
186
0
            pafLine[iPixel * 2 + 0] = (float)(SQRT_2 * (m[M13] + m[M23]));
187
188
            // imaginary
189
0
            pafLine[iPixel * 2 + 1] = (float)(-SQRT_2 * (m[M24] + m[M14]));
190
0
        }
191
0
    }
192
0
    else if (nBand == 3) /* C13 */
193
0
    {
194
0
        for (int iPixel = 0; iPixel < nRasterXSize; iPixel++)
195
0
        {
196
0
            double *m = padfMatrix + 10 * iPixel;
197
198
            // real
199
0
            pafLine[iPixel * 2 + 0] = (float)(2 * m[M33] + m[M22] - m[M11]);
200
201
            // imaginary
202
0
            pafLine[iPixel * 2 + 1] = (float)(-2 * m[M34]);
203
0
        }
204
0
    }
205
0
    else if (nBand == 4) /* C22 */
206
0
    {
207
0
        for (int iPixel = 0; iPixel < nRasterXSize; iPixel++)
208
0
        {
209
0
            double *m = padfMatrix + 10 * iPixel;
210
211
0
            pafLine[iPixel * 2 + 0] = (float)(2 * (m[M11] - m[M22]));
212
0
            pafLine[iPixel * 2 + 1] = 0.0;
213
0
        }
214
0
    }
215
0
    else if (nBand == 5) /* C23 */
216
0
    {
217
0
        for (int iPixel = 0; iPixel < nRasterXSize; iPixel++)
218
0
        {
219
0
            double *m = padfMatrix + 10 * iPixel;
220
221
            // real
222
0
            pafLine[iPixel * 2 + 0] = (float)(SQRT_2 * (m[M13] - m[M23]));
223
224
            // imaginary
225
0
            pafLine[iPixel * 2 + 1] = (float)(SQRT_2 * (m[M24] - m[M14]));
226
0
        }
227
0
    }
228
0
    else if (nBand == 6) /* C33 */
229
0
    {
230
0
        for (int iPixel = 0; iPixel < nRasterXSize; iPixel++)
231
0
        {
232
0
            double *m = padfMatrix + 10 * iPixel;
233
234
0
            pafLine[iPixel * 2 + 0] = (float)(m[M11] + m[M22] - 2 * m[M12]);
235
0
            pafLine[iPixel * 2 + 1] = 0.0;
236
0
        }
237
0
    }
238
239
0
    return CE_None;
240
0
}
241
242
/************************************************************************/
243
/* ==================================================================== */
244
/*                              AirSARDataset                           */
245
/* ==================================================================== */
246
/************************************************************************/
247
248
/************************************************************************/
249
/*                           AirSARDataset()                            */
250
/************************************************************************/
251
252
AirSARDataset::AirSARDataset()
253
0
    : fp(nullptr), nLoadedLine(-1), pabyCompressedLine(nullptr),
254
0
      padfMatrix(nullptr), nDataStart(0), nRecordLength(0)
255
0
{
256
0
}
257
258
/************************************************************************/
259
/*                           ~AirSARDataset()                           */
260
/************************************************************************/
261
262
AirSARDataset::~AirSARDataset()
263
264
0
{
265
0
    FlushCache(true);
266
0
    CPLFree(pabyCompressedLine);
267
0
    CPLFree(padfMatrix);
268
269
0
    if (fp != nullptr)
270
0
    {
271
0
        VSIFCloseL(fp);
272
0
        fp = nullptr;
273
0
    }
274
0
}
275
276
/************************************************************************/
277
/*                              LoadLine()                              */
278
/************************************************************************/
279
280
CPLErr AirSARDataset::LoadLine(int iLine)
281
282
0
{
283
0
    if (iLine == nLoadedLine)
284
0
        return CE_None;
285
286
    /* -------------------------------------------------------------------- */
287
    /*      allocate working buffers if we don't have them already.         */
288
    /* -------------------------------------------------------------------- */
289
0
    if (pabyCompressedLine == nullptr)
290
0
    {
291
0
        pabyCompressedLine = (GByte *)VSI_MALLOC2_VERBOSE(nRasterXSize, 10);
292
293
0
        padfMatrix =
294
0
            (double *)VSI_MALLOC2_VERBOSE(10 * sizeof(double), nRasterXSize);
295
0
        if (pabyCompressedLine == nullptr || padfMatrix == nullptr)
296
0
        {
297
0
            CPLFree(pabyCompressedLine);
298
0
            CPLFree(padfMatrix);
299
0
            return CE_Failure;
300
0
        }
301
0
    }
302
303
0
    CPLAssert(nRecordLength == nRasterXSize * 10);
304
305
    /* -------------------------------------------------------------------- */
306
    /*      Load raw compressed data.                                       */
307
    /* -------------------------------------------------------------------- */
308
0
    if (VSIFSeekL(fp, nDataStart + iLine * nRecordLength, SEEK_SET) != 0 ||
309
0
        ((int)VSIFReadL(pabyCompressedLine, 10, nRasterXSize, fp)) !=
310
0
            nRasterXSize)
311
0
    {
312
0
        CPLError(CE_Failure, CPLE_FileIO,
313
0
                 "Error reading %d bytes for line %d at offset %d.\n%s",
314
0
                 nRasterXSize * 10, iLine, nDataStart + iLine * nRecordLength,
315
0
                 VSIStrerror(errno));
316
0
        return CE_Failure;
317
0
    }
318
319
    /* -------------------------------------------------------------------- */
320
    /*      Build stokes matrix                                             */
321
    /* -------------------------------------------------------------------- */
322
0
    for (int iPixel = 0; iPixel < nRasterXSize; iPixel++)
323
0
    {
324
0
        double *M = padfMatrix + 10 * iPixel;
325
0
        signed char *byte = (signed char *)pabyCompressedLine + 10 * iPixel - 1;
326
0
        const double gen_fac = 1.0;  // should we have a general scale factor?
327
328
0
        M[M11] = (byte[2] / 254.0 + 1.5) * pow(2.0, byte[1]) * gen_fac;
329
0
        M[M12] = byte[3] * M[M11] / 127.0;
330
0
        M[M13] = byte[4] * fabs((double)byte[4]) * M[M11] / (127 * 127);
331
0
        M[M14] = byte[5] * fabs((double)byte[5]) * M[M11] / (127 * 127);
332
0
        M[M23] = byte[6] * fabs((double)byte[6]) * M[M11] / (127 * 127);
333
0
        M[M24] = byte[7] * fabs((double)byte[7]) * M[M11] / (127 * 127);
334
0
        M[M33] = byte[8] * M[M11] / 127;
335
0
        M[M34] = byte[9] * M[M11] / 127;
336
0
        M[M44] = byte[10] * M[M11] / 127;
337
0
        M[M22] = M[M11] - M[M33] - M[M44];
338
0
    }
339
340
0
    return CE_None;
341
0
}
342
343
/************************************************************************/
344
/*                             ReadHeader()                             */
345
/*                                                                      */
346
/*      Read the AirSAR header.  We assume an equal sign separates      */
347
/*      the keyword name from the value.  If not, assume the last       */
348
/*      "blank delimited" word is the value and everything else is a    */
349
/*      keyword.                                                        */
350
/*                                                                      */
351
/*      The records are 50 characters each.  Read till we get an all    */
352
/*      blank record or some zero bytes.                                */
353
/************************************************************************/
354
355
char **AirSARDataset::ReadHeader(VSILFILE *fp, vsi_l_offset nFileOffset,
356
                                 const char *pszPrefix, int nMaxLines)
357
358
0
{
359
0
    char **papszHeadInfo = nullptr;
360
0
    char szLine[51];
361
362
0
    VSIFSeekL(fp, nFileOffset, SEEK_SET);
363
364
    /* ==================================================================== */
365
    /*      Loop collecting one line at a time.                             */
366
    /* ==================================================================== */
367
0
    for (int iLine = 0; iLine < nMaxLines; iLine++)
368
0
    {
369
        /* --------------------------------------------------------------------
370
         */
371
        /*      Read a 50 byte header record. */
372
        /* --------------------------------------------------------------------
373
         */
374
0
        if (VSIFReadL(szLine, 1, 50, fp) != 50)
375
0
        {
376
0
            CPLError(CE_Failure, CPLE_FileIO,
377
0
                     "Read error collecting AirSAR header.");
378
0
            CSLDestroy(papszHeadInfo);
379
0
            return nullptr;
380
0
        }
381
382
0
        szLine[50] = '\0';
383
384
        /* --------------------------------------------------------------------
385
         */
386
        /*      Is it all spaces, or does it have a zero byte? */
387
        /* --------------------------------------------------------------------
388
         */
389
0
        bool bAllSpaces = true;
390
0
        bool bHasIllegalChars = false;
391
392
0
        for (int i = 0; i < 50; i++)
393
0
        {
394
0
            if (szLine[i] == '\0')
395
0
                break;
396
397
0
            if (szLine[i] != ' ')
398
0
                bAllSpaces = false;
399
400
0
            if (((unsigned char *)szLine)[i] > 127 ||
401
0
                ((unsigned char *)szLine)[i] < 10)
402
0
                bHasIllegalChars = true;
403
0
        }
404
405
0
        if (bAllSpaces || bHasIllegalChars)
406
0
            break;
407
408
        /* --------------------------------------------------------------------
409
         */
410
        /*      Find the pivot between the keyword name and value. */
411
        /* --------------------------------------------------------------------
412
         */
413
0
        int iPivot = -1;
414
415
0
        for (int i = 0; i < 50; i++)
416
0
        {
417
0
            if (szLine[i] == '=')
418
0
            {
419
0
                iPivot = i;
420
0
                break;
421
0
            }
422
0
        }
423
424
        // If no "=" found, split on first double white space
425
0
        if (iPivot == -1)
426
0
        {
427
0
            for (int i = 48; i >= 0; i--)
428
0
            {
429
0
                if (szLine[i] == ' ' && szLine[i + 1] == ' ')
430
0
                {
431
0
                    iPivot = i;
432
0
                    break;
433
0
                }
434
0
            }
435
0
        }
436
437
0
        if (iPivot == -1)  // Yikes!
438
0
        {
439
0
            CPLDebug("AIRSAR", "No pivot in line `%s'.", szLine);
440
0
            CPLAssert(iPivot != -1);
441
0
            break;
442
0
        }
443
444
        /* --------------------------------------------------------------------
445
         */
446
        /*      Trace ahead to the first non-white space value character. */
447
        /* --------------------------------------------------------------------
448
         */
449
0
        int iValue = iPivot + 1;
450
451
0
        while (iValue < 50 && szLine[iValue] == ' ')
452
0
            iValue++;
453
454
        /* --------------------------------------------------------------------
455
         */
456
        /*      Strip any white space off the keyword. */
457
        /* --------------------------------------------------------------------
458
         */
459
0
        int iKeyEnd = iPivot - 1;
460
461
0
        while (iKeyEnd > 0 && szLine[iKeyEnd] == ' ')
462
0
            iKeyEnd--;
463
464
0
        szLine[iKeyEnd + 1] = '\0';
465
466
        /* --------------------------------------------------------------------
467
         */
468
        /*      Convert spaces or colons into underscores in the key name. */
469
        /* --------------------------------------------------------------------
470
         */
471
0
        for (int i = 0; szLine[i] != '\0'; i++)
472
0
        {
473
0
            if (szLine[i] == ' ' || szLine[i] == ':' || szLine[i] == ',')
474
0
                szLine[i] = '_';
475
0
        }
476
477
        /* --------------------------------------------------------------------
478
         */
479
        /*      Prefix key name with provided prefix string. */
480
        /* --------------------------------------------------------------------
481
         */
482
0
        char szPrefixedKeyName[55];
483
484
0
        snprintf(szPrefixedKeyName, sizeof(szPrefixedKeyName), "%s_%s",
485
0
                 pszPrefix, szLine);
486
487
0
        papszHeadInfo =
488
0
            CSLSetNameValue(papszHeadInfo, szPrefixedKeyName, szLine + iValue);
489
0
    }
490
491
0
    return papszHeadInfo;
492
0
}
493
494
/************************************************************************/
495
/*                                Open()                                */
496
/************************************************************************/
497
498
GDALDataset *AirSARDataset::Open(GDALOpenInfo *poOpenInfo)
499
500
529k
{
501
529k
    if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 800)
502
436k
        return nullptr;
503
504
    /* -------------------------------------------------------------------- */
505
    /*      Check for AirSAR/ keyword.                                      */
506
    /* -------------------------------------------------------------------- */
507
93.0k
    if (!STARTS_WITH_CI((char *)poOpenInfo->pabyHeader,
508
93.0k
                        "RECORD LENGTH IN BYTES"))
509
93.0k
        return nullptr;
510
511
0
    if (strstr((char *)poOpenInfo->pabyHeader, "COMPRESSED") == nullptr ||
512
0
        strstr((char *)poOpenInfo->pabyHeader, "JPL AIRCRAFT") == nullptr)
513
0
        return nullptr;
514
515
    /* -------------------------------------------------------------------- */
516
    /*      Parse the header fields.  We turn all the transform the         */
517
    /*      keywords by converting spaces to underscores so they will be    */
518
    /*      "well behaved" as metadata keywords.                            */
519
    /* -------------------------------------------------------------------- */
520
0
    char **papszMD = ReadHeader(poOpenInfo->fpL, 0, "MH", 20);
521
522
0
    if (papszMD == nullptr)
523
0
        return nullptr;
524
525
    /* -------------------------------------------------------------------- */
526
    /*      Confirm the requested access is supported.                      */
527
    /* -------------------------------------------------------------------- */
528
0
    if (poOpenInfo->eAccess == GA_Update)
529
0
    {
530
0
        ReportUpdateNotSupportedByDriver("AIRSAR");
531
0
        CSLDestroy(papszMD);
532
0
        return nullptr;
533
0
    }
534
    /* -------------------------------------------------------------------- */
535
    /*      Create a corresponding GDALDataset.                             */
536
    /* -------------------------------------------------------------------- */
537
0
    AirSARDataset *poDS = new AirSARDataset();
538
539
    /* -------------------------------------------------------------------- */
540
    /*      Extract some key information.                                   */
541
    /* -------------------------------------------------------------------- */
542
543
0
    poDS->nRasterXSize =
544
0
        atoi(CSLFetchNameValue(papszMD, "MH_NUMBER_OF_SAMPLES_PER_RECORD"));
545
0
    poDS->nRasterYSize =
546
0
        atoi(CSLFetchNameValue(papszMD, "MH_NUMBER_OF_LINES_IN_IMAGE"));
547
548
0
    poDS->nRecordLength =
549
0
        atoi(CSLFetchNameValue(papszMD, "MH_RECORD_LENGTH_IN_BYTES"));
550
551
0
    poDS->nDataStart =
552
0
        atoi(CSLFetchNameValue(papszMD, "MH_BYTE_OFFSET_OF_FIRST_DATA_RECORD"));
553
554
    /* -------------------------------------------------------------------- */
555
    /*      Adopt the openinfo file pointer.                                */
556
    /* -------------------------------------------------------------------- */
557
0
    poDS->fp = poOpenInfo->fpL;
558
0
    poOpenInfo->fpL = nullptr;
559
560
    /* -------------------------------------------------------------------- */
561
    /*      Read and merge parameter header into metadata.  Prefix          */
562
    /*      parameter header values with PH_.                               */
563
    /* -------------------------------------------------------------------- */
564
0
    vsi_l_offset nPHOffset = 0;
565
566
0
    if (CSLFetchNameValue(papszMD, "MH_BYTE_OFFSET_OF_PARAMETER_HEADER") !=
567
0
        nullptr)
568
0
    {
569
0
        nPHOffset = std::strtoull(
570
0
            CSLFetchNameValue(papszMD, "MH_BYTE_OFFSET_OF_PARAMETER_HEADER"),
571
0
            nullptr, 0);
572
0
        char **papszPHInfo = ReadHeader(poDS->fp, nPHOffset, "PH", 100);
573
574
0
        papszMD = CSLInsertStrings(papszMD, CSLCount(papszMD), papszPHInfo);
575
576
0
        CSLDestroy(papszPHInfo);
577
0
    }
578
579
    /* -------------------------------------------------------------------- */
580
    /*      Read and merge calibration header into metadata.  Prefix        */
581
    /*      parameter header values with CH_.                               */
582
    /* -------------------------------------------------------------------- */
583
0
    if (nPHOffset != 0)
584
0
    {
585
0
        char **papszCHInfo =
586
0
            ReadHeader(poDS->fp, nPHOffset + poDS->nRecordLength, "CH", 18);
587
588
0
        papszMD = CSLInsertStrings(papszMD, CSLCount(papszMD), papszCHInfo);
589
590
0
        CSLDestroy(papszCHInfo);
591
0
    }
592
593
    /* -------------------------------------------------------------------- */
594
    /*      Assign metadata to dataset.                                     */
595
    /* -------------------------------------------------------------------- */
596
0
    poDS->SetMetadata(papszMD);
597
0
    CSLDestroy(papszMD);
598
599
    /* -------------------------------------------------------------------- */
600
    /*      Create band information objects.                                */
601
    /* -------------------------------------------------------------------- */
602
0
    poDS->SetBand(1, new AirSARRasterBand(poDS, 1));
603
0
    poDS->SetBand(2, new AirSARRasterBand(poDS, 2));
604
0
    poDS->SetBand(3, new AirSARRasterBand(poDS, 3));
605
0
    poDS->SetBand(4, new AirSARRasterBand(poDS, 4));
606
0
    poDS->SetBand(5, new AirSARRasterBand(poDS, 5));
607
0
    poDS->SetBand(6, new AirSARRasterBand(poDS, 6));
608
609
0
    poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SYMMETRIZED_COVARIANCE");
610
611
    /* -------------------------------------------------------------------- */
612
    /*      Initialize any PAM information.                                 */
613
    /* -------------------------------------------------------------------- */
614
0
    poDS->SetDescription(poOpenInfo->pszFilename);
615
0
    poDS->TryLoadXML();
616
617
0
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
618
619
0
    return poDS;
620
0
}
621
622
/************************************************************************/
623
/*                        GDALRegister_AirSAR()                         */
624
/************************************************************************/
625
626
void GDALRegister_AirSAR()
627
628
24
{
629
24
    if (GDALGetDriverByName("AirSAR") != nullptr)
630
0
        return;
631
632
24
    GDALDriver *poDriver = new GDALDriver();
633
634
24
    poDriver->SetDescription("AirSAR");
635
24
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
636
24
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "AirSAR Polarimetric Image");
637
24
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/airsar.html");
638
639
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
640
641
24
    poDriver->pfnOpen = AirSARDataset::Open;
642
643
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
644
24
}