Coverage Report

Created: 2025-06-09 08:44

/src/gdal/frmts/pcidsk/pcidskdataset2.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  PCIDSK Database File
4
 * Purpose:  Read/write PCIDSK Database File used by the PCI software, using
5
 *           the external PCIDSK library.
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
10
 * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14
15
#include "gdal_frmts.h"
16
#include "pcidskdataset2.h"
17
#include "pcidskdrivercore.h"
18
19
#include <algorithm>
20
21
const PCIDSK::PCIDSKInterfaces *PCIDSK2GetInterfaces(void);
22
23
/************************************************************************/
24
/* ==================================================================== */
25
/*                            PCIDSK2Band                               */
26
/* ==================================================================== */
27
/************************************************************************/
28
29
/************************************************************************/
30
/*                            PCIDSK2Band()                             */
31
/*                                                                      */
32
/*      This constructor is used for main file channels.                */
33
/************************************************************************/
34
35
PCIDSK2Band::PCIDSK2Band(PCIDSKFile *poFileIn, PCIDSKChannel *poChannelIn)
36
37
7.62k
{
38
7.62k
    Initialize();
39
40
7.62k
    poFile = poFileIn;
41
7.62k
    poChannel = poChannelIn;
42
43
7.62k
    nBlockXSize = static_cast<int>(poChannel->GetBlockWidth());
44
7.62k
    nBlockYSize = static_cast<int>(poChannel->GetBlockHeight());
45
46
7.62k
    eDataType = PCIDSK2Dataset::PCIDSKTypeToGDAL(poChannel->GetType());
47
48
7.62k
    if (!STARTS_WITH_CI(poChannel->GetDescription().c_str(),
49
7.62k
                        "Contents Not Specified"))
50
7.61k
        GDALMajorObject::SetDescription(poChannel->GetDescription().c_str());
51
52
    /* -------------------------------------------------------------------- */
53
    /*      Do we have overviews?                                           */
54
    /* -------------------------------------------------------------------- */
55
7.62k
    RefreshOverviewList();
56
7.62k
}
57
58
/************************************************************************/
59
/*                            PCIDSK2Band()                             */
60
/*                                                                      */
61
/*      This constructor is used for overviews and bitmap segments      */
62
/*      as bands.                                                       */
63
/************************************************************************/
64
65
PCIDSK2Band::PCIDSK2Band(PCIDSKChannel *poChannelIn)
66
67
15
{
68
15
    Initialize();
69
70
15
    this->poChannel = poChannelIn;
71
72
15
    nBand = 1;
73
74
15
    nBlockXSize = static_cast<int>(poChannel->GetBlockWidth());
75
15
    nBlockYSize = static_cast<int>(poChannel->GetBlockHeight());
76
77
15
    nRasterXSize = static_cast<int>(poChannel->GetWidth());
78
15
    nRasterYSize = static_cast<int>(poChannel->GetHeight());
79
80
15
    eDataType = PCIDSK2Dataset::PCIDSKTypeToGDAL(poChannel->GetType());
81
82
15
    if (poChannel->GetType() == CHN_BIT)
83
0
    {
84
0
        PCIDSK2Band::SetMetadataItem("NBITS", "1", "IMAGE_STRUCTURE");
85
86
0
        if (!STARTS_WITH_CI(poChannel->GetDescription().c_str(),
87
0
                            "Contents Not Specified"))
88
0
            GDALMajorObject::SetDescription(
89
0
                poChannel->GetDescription().c_str());
90
0
    }
91
15
}
92
93
/************************************************************************/
94
/*                             Initialize()                             */
95
/************************************************************************/
96
97
void PCIDSK2Band::Initialize()
98
99
7.64k
{
100
7.64k
    papszLastMDListValue = nullptr;
101
102
7.64k
    poChannel = nullptr;
103
7.64k
    poFile = nullptr;
104
7.64k
    poDS = nullptr;
105
106
7.64k
    bCheckedForColorTable = false;
107
7.64k
    poColorTable = nullptr;
108
7.64k
    nPCTSegNumber = -1;
109
110
7.64k
    papszCategoryNames = nullptr;
111
7.64k
}
112
113
/************************************************************************/
114
/*                            ~PCIDSK2Band()                            */
115
/************************************************************************/
116
117
PCIDSK2Band::~PCIDSK2Band()
118
119
7.61k
{
120
7.62k
    while (!apoOverviews.empty())
121
10
    {
122
10
        delete apoOverviews.back();
123
10
        apoOverviews.pop_back();
124
10
    }
125
7.61k
    CSLDestroy(papszLastMDListValue);
126
7.61k
    CSLDestroy(papszCategoryNames);
127
128
7.61k
    delete poColorTable;
129
7.61k
}
130
131
/************************************************************************/
132
/*                           SetDescription()                           */
133
/************************************************************************/
134
135
void PCIDSK2Band::SetDescription(const char *pszDescription)
136
137
0
{
138
0
    if (GetAccess() == GA_ReadOnly)
139
0
    {
140
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
141
0
                 "Unable to set description on read-only file.");
142
0
        return;
143
0
    }
144
145
0
    try
146
0
    {
147
0
        poChannel->SetDescription(pszDescription);
148
149
0
        if (!STARTS_WITH_CI(poChannel->GetDescription().c_str(),
150
0
                            "Contents Not Specified"))
151
0
            GDALMajorObject::SetDescription(
152
0
                poChannel->GetDescription().c_str());
153
0
    }
154
0
    catch (const PCIDSKException &ex)
155
0
    {
156
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
157
0
    }
158
0
}
159
160
/************************************************************************/
161
/*                          GetCategoryNames()                          */
162
/*                                                                      */
163
/*      Offer category names from Class_*_ metadata.                    */
164
/************************************************************************/
165
166
char **PCIDSK2Band::GetCategoryNames()
167
168
0
{
169
    // already scanned?
170
0
    if (papszCategoryNames != nullptr)
171
0
        return papszCategoryNames;
172
173
0
    try
174
0
    {
175
0
        std::vector<std::string> aosMDKeys = poChannel->GetMetadataKeys();
176
0
        int nClassCount = 0;
177
0
        constexpr int nMaxClasses = 10000;
178
0
        papszCategoryNames = reinterpret_cast<char **>(
179
0
            CPLCalloc(nMaxClasses + 1, sizeof(char *)));
180
181
0
        for (size_t i = 0; i < aosMDKeys.size(); i++)
182
0
        {
183
0
            CPLString osKey = aosMDKeys[i];
184
185
            // is this a "Class_n_name" keyword?
186
0
            if (!STARTS_WITH_CI(osKey, "Class_"))
187
0
                continue;
188
189
0
            if (!EQUAL(osKey.c_str() + osKey.size() - 5, "_name"))
190
0
                continue;
191
192
            // Ignore unreasonable class values.
193
0
            int iClass = atoi(osKey.c_str() + 6);
194
195
0
            if (iClass < 0 || iClass > 10000)
196
0
                continue;
197
198
            // Fetch the name.
199
0
            CPLString osName = poChannel->GetMetadataValue(osKey);
200
201
            // do we need to put in place dummy class names for missing values?
202
0
            if (iClass >= nClassCount)
203
0
            {
204
0
                while (iClass >= nClassCount)
205
0
                {
206
0
                    papszCategoryNames[nClassCount++] = CPLStrdup("");
207
0
                    papszCategoryNames[nClassCount] = nullptr;
208
0
                }
209
0
            }
210
211
            // Replace target category name.
212
0
            CPLFree(papszCategoryNames[iClass]);
213
0
            papszCategoryNames[iClass] = nullptr;
214
215
0
            papszCategoryNames[iClass] = CPLStrdup(osName);
216
0
        }
217
218
0
        if (nClassCount == 0)
219
0
            return GDALPamRasterBand::GetCategoryNames();
220
221
0
        return papszCategoryNames;
222
0
    }
223
0
    catch (const PCIDSKException &ex)
224
0
    {
225
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
226
0
        return nullptr;
227
0
    }
228
0
}
229
230
/************************************************************************/
231
/*                         CheckForColorTable()                         */
232
/************************************************************************/
233
234
bool PCIDSK2Band::CheckForColorTable()
235
236
69
{
237
69
    if (bCheckedForColorTable || poFile == nullptr)
238
0
        return true;
239
240
69
    bCheckedForColorTable = true;
241
242
69
    try
243
69
    {
244
        /* --------------------------------------------------------------------
245
         */
246
        /*      Try to find an appropriate PCT segment to use. */
247
        /* --------------------------------------------------------------------
248
         */
249
69
        std::string osDefaultPCT =
250
69
            poChannel->GetMetadataValue("DEFAULT_PCT_REF");
251
69
        PCIDSKSegment *poPCTSeg = nullptr;
252
253
        // If there is no metadata, assume a single PCT in a file with only
254
        // one raster band must be intended for it.
255
69
        if (osDefaultPCT.empty() && poDS != nullptr &&
256
69
            poDS->GetRasterCount() == 1)
257
0
        {
258
0
            poPCTSeg = poFile->GetSegment(SEG_PCT, "");
259
0
            if (poPCTSeg != nullptr &&
260
0
                poFile->GetSegment(SEG_PCT, "", poPCTSeg->GetSegmentNumber()) !=
261
0
                    nullptr)
262
0
                poPCTSeg = nullptr;
263
0
        }
264
        // Parse default PCT ref assuming an in file reference.
265
69
        else if (!osDefaultPCT.empty() &&
266
69
                 strstr(osDefaultPCT.c_str(), "PCT:") != nullptr)
267
0
        {
268
0
            poPCTSeg = poFile->GetSegment(
269
0
                atoi(strstr(osDefaultPCT.c_str(), "PCT:") + 4));
270
0
        }
271
272
69
        if (poPCTSeg != nullptr)
273
0
        {
274
0
            poColorTable = new GDALColorTable();
275
0
            unsigned char abyPCT[768];
276
277
0
            PCIDSK_PCT *poPCT = dynamic_cast<PCIDSK_PCT *>(poPCTSeg);
278
0
            if (poPCT)
279
0
            {
280
0
                nPCTSegNumber = poPCTSeg->GetSegmentNumber();
281
282
0
                poPCT->ReadPCT(abyPCT);
283
284
0
                for (int i = 0; i < 256; i++)
285
0
                {
286
0
                    GDALColorEntry sEntry;
287
288
0
                    sEntry.c1 = abyPCT[256 * 0 + i];
289
0
                    sEntry.c2 = abyPCT[256 * 1 + i];
290
0
                    sEntry.c3 = abyPCT[256 * 2 + i];
291
0
                    sEntry.c4 = 255;
292
0
                    poColorTable->SetColorEntry(i, &sEntry);
293
0
                }
294
0
            }
295
0
        }
296
297
        /* --------------------------------------------------------------------
298
         */
299
        /*      If we did not find an appropriate PCT segment, check for */
300
        /*      Class_n color data from which to construct a color table. */
301
        /* --------------------------------------------------------------------
302
         */
303
69
        std::vector<std::string> aosMDKeys = poChannel->GetMetadataKeys();
304
305
69
        for (size_t i = 0; i < aosMDKeys.size(); i++)
306
0
        {
307
0
            CPLString osKey = aosMDKeys[i];
308
309
            // is this a "Class_n_name" keyword?
310
311
0
            if (!STARTS_WITH_CI(osKey, "Class_"))
312
0
                continue;
313
314
0
            if (!EQUAL(osKey.c_str() + osKey.size() - 6, "_Color"))
315
0
                continue;
316
317
            // Ignore unreasonable class values.
318
0
            const int iClass = atoi(osKey.c_str() + 6);
319
320
0
            if (iClass < 0 || iClass > 10000)
321
0
                continue;
322
323
            // Fetch and parse the RGB value "(RGB:red green blue)"
324
0
            CPLString osRGB = poChannel->GetMetadataValue(osKey);
325
326
0
            if (!STARTS_WITH_CI(osRGB, "(RGB:"))
327
0
                continue;
328
329
0
            int nRed, nGreen, nBlue;
330
0
            if (sscanf(osRGB.c_str() + 5, "%d %d %d", &nRed, &nGreen, &nBlue) !=
331
0
                3)
332
0
                continue;
333
334
            // we have an entry - apply to the color table.
335
0
            GDALColorEntry sEntry;
336
337
0
            sEntry.c1 = (short)nRed;
338
0
            sEntry.c2 = (short)nGreen;
339
0
            sEntry.c3 = (short)nBlue;
340
0
            sEntry.c4 = 255;
341
342
0
            if (poColorTable == nullptr)
343
0
            {
344
0
                CPLDebug("PCIDSK",
345
0
                         "Using Class_n_Color metadata for color table.");
346
0
                poColorTable = new GDALColorTable();
347
0
            }
348
349
0
            poColorTable->SetColorEntry(iClass, &sEntry);
350
0
        }
351
69
    }
352
69
    catch (const PCIDSKException &ex)
353
69
    {
354
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
355
0
        return false;
356
0
    }
357
358
69
    return true;
359
69
}
360
361
/************************************************************************/
362
/*                           GetColorTable()                            */
363
/************************************************************************/
364
365
GDALColorTable *PCIDSK2Band::GetColorTable()
366
367
0
{
368
0
    CheckForColorTable();
369
370
0
    if (poColorTable)
371
0
        return poColorTable;
372
373
0
    return GDALPamRasterBand::GetColorTable();
374
0
}
375
376
/************************************************************************/
377
/*                           SetColorTable()                            */
378
/************************************************************************/
379
380
CPLErr PCIDSK2Band::SetColorTable(GDALColorTable *poCT)
381
382
0
{
383
0
    if (!CheckForColorTable())
384
0
        return CE_Failure;
385
386
    // no color tables on overviews.
387
0
    if (poFile == nullptr)
388
0
        return CE_Failure;
389
390
0
    if (GetAccess() == GA_ReadOnly)
391
0
    {
392
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
393
0
                 "Unable to set color table on read-only file.");
394
0
        return CE_Failure;
395
0
    }
396
397
0
    try
398
0
    {
399
        /* --------------------------------------------------------------------
400
         */
401
        /*      Are we trying to delete the color table? */
402
        /* --------------------------------------------------------------------
403
         */
404
0
        if (poCT == nullptr)
405
0
        {
406
0
            delete poColorTable;
407
0
            poColorTable = nullptr;
408
409
0
            if (nPCTSegNumber != -1)
410
0
                poFile->DeleteSegment(nPCTSegNumber);
411
0
            poChannel->SetMetadataValue("DEFAULT_PCT_REF", "");
412
0
            nPCTSegNumber = -1;
413
414
0
            return CE_None;
415
0
        }
416
417
        /* --------------------------------------------------------------------
418
         */
419
        /*      Do we need to create the segment?  If so, also set the */
420
        /*      default pct metadata. */
421
        /* --------------------------------------------------------------------
422
         */
423
0
        if (nPCTSegNumber == -1)
424
0
        {
425
0
            nPCTSegNumber = poFile->CreateSegment(
426
0
                "PCTTable", "Default Pseudo-Color Table", SEG_PCT, 0);
427
428
0
            CPLString osRef;
429
0
            osRef.Printf("gdb:/{PCT:%d}", nPCTSegNumber);
430
0
            poChannel->SetMetadataValue("DEFAULT_PCT_REF", osRef);
431
0
        }
432
433
        /* --------------------------------------------------------------------
434
         */
435
        /*      Write out the PCT. */
436
        /* --------------------------------------------------------------------
437
         */
438
0
        const int nColorCount = std::min(256, poCT->GetColorEntryCount());
439
440
0
        unsigned char abyPCT[768];
441
0
        memset(abyPCT, 0, 768);
442
443
0
        for (int i = 0; i < nColorCount; i++)
444
0
        {
445
0
            GDALColorEntry sEntry;
446
447
0
            poCT->GetColorEntryAsRGB(i, &sEntry);
448
0
            abyPCT[256 * 0 + i] = (unsigned char)sEntry.c1;
449
0
            abyPCT[256 * 1 + i] = (unsigned char)sEntry.c2;
450
0
            abyPCT[256 * 2 + i] = (unsigned char)sEntry.c3;
451
0
        }
452
453
0
        PCIDSK_PCT *poPCT =
454
0
            dynamic_cast<PCIDSK_PCT *>(poFile->GetSegment(nPCTSegNumber));
455
0
        if (poPCT)
456
0
            poPCT->WritePCT(abyPCT);
457
458
0
        delete poColorTable;
459
0
        poColorTable = poCT->Clone();
460
0
    }
461
462
    /* -------------------------------------------------------------------- */
463
    /*      Trap exceptions.                                                */
464
    /* -------------------------------------------------------------------- */
465
0
    catch (const PCIDSKException &ex)
466
0
    {
467
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
468
0
        return CE_Failure;
469
0
    }
470
471
0
    return CE_None;
472
0
}
473
474
/************************************************************************/
475
/*                       GetColorInterpretation()                       */
476
/************************************************************************/
477
478
GDALColorInterp PCIDSK2Band::GetColorInterpretation()
479
480
69
{
481
69
    CheckForColorTable();
482
483
69
    if (poColorTable != nullptr)
484
0
        return GCI_PaletteIndex;
485
486
69
    return GDALPamRasterBand::GetColorInterpretation();
487
69
}
488
489
/************************************************************************/
490
/*                        RefreshOverviewList()                         */
491
/************************************************************************/
492
493
void PCIDSK2Band::RefreshOverviewList()
494
495
7.62k
{
496
    /* -------------------------------------------------------------------- */
497
    /*      Clear existing overviews.                                       */
498
    /* -------------------------------------------------------------------- */
499
7.62k
    while (!apoOverviews.empty())
500
0
    {
501
0
        delete apoOverviews.back();
502
0
        apoOverviews.pop_back();
503
0
    }
504
505
    /* -------------------------------------------------------------------- */
506
    /*      Fetch overviews.                                                */
507
    /* -------------------------------------------------------------------- */
508
7.64k
    for (int iOver = 0; iOver < poChannel->GetOverviewCount(); iOver++)
509
15
    {
510
15
        auto poOvrBand = new PCIDSK2Band(poChannel->GetOverview(iOver));
511
15
        poOvrBand->eAccess = eAccess;
512
15
        apoOverviews.push_back(poOvrBand);
513
15
    }
514
7.62k
}
515
516
/************************************************************************/
517
/*                             IReadBlock()                             */
518
/************************************************************************/
519
520
CPLErr PCIDSK2Band::IReadBlock(int iBlockX, int iBlockY, void *pData)
521
522
6.99k
{
523
6.99k
    try
524
6.99k
    {
525
6.99k
        poChannel->ReadBlock(iBlockX + iBlockY * nBlocksPerRow, pData);
526
527
        // Do we need to upsample 1bit to 8bit?
528
6.99k
        if (poChannel->GetType() == CHN_BIT)
529
0
        {
530
0
            GByte *pabyData = reinterpret_cast<GByte *>(pData);
531
532
0
            for (int ii = nBlockXSize * nBlockYSize - 1; ii >= 0; ii--)
533
0
            {
534
0
                if ((pabyData[ii >> 3] & (0x80 >> (ii & 0x7))))
535
0
                    pabyData[ii] = 1;
536
0
                else
537
0
                    pabyData[ii] = 0;
538
0
            }
539
0
        }
540
541
6.99k
        return CE_None;
542
6.99k
    }
543
6.99k
    catch (const PCIDSKException &ex)
544
6.99k
    {
545
1.38k
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
546
1.38k
        return CE_Failure;
547
1.38k
    }
548
6.99k
}
549
550
/************************************************************************/
551
/*                             IWriteBlock()                            */
552
/************************************************************************/
553
554
CPLErr PCIDSK2Band::IWriteBlock(int iBlockX, int iBlockY, void *pData)
555
556
0
{
557
0
    try
558
0
    {
559
0
        poChannel->WriteBlock(iBlockX + iBlockY * nBlocksPerRow, pData);
560
0
    }
561
0
    catch (const PCIDSKException &ex)
562
0
    {
563
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
564
0
        return CE_Failure;
565
0
    }
566
567
0
    return CE_None;
568
0
}
569
570
/************************************************************************/
571
/*                          GetOverviewCount()                          */
572
/************************************************************************/
573
574
int PCIDSK2Band::GetOverviewCount()
575
576
377
{
577
377
    if (!apoOverviews.empty())
578
10
        return static_cast<int>(apoOverviews.size());
579
580
367
    return GDALPamRasterBand::GetOverviewCount();
581
377
}
582
583
/************************************************************************/
584
/*                            GetOverview()                             */
585
/************************************************************************/
586
587
GDALRasterBand *PCIDSK2Band::GetOverview(int iOverview)
588
589
10
{
590
10
    if (iOverview < 0 || iOverview >= static_cast<int>(apoOverviews.size()))
591
0
        return GDALPamRasterBand::GetOverview(iOverview);
592
593
10
    return apoOverviews[iOverview];
594
10
}
595
596
/************************************************************************/
597
/*                            SetMetadata()                             */
598
/************************************************************************/
599
600
CPLErr PCIDSK2Band::SetMetadata(char **papszMD, const char *pszDomain)
601
602
0
{
603
    /* -------------------------------------------------------------------- */
604
    /*      PCIDSK only supports metadata in the default domain.            */
605
    /* -------------------------------------------------------------------- */
606
0
    if (pszDomain != nullptr && strlen(pszDomain) > 0)
607
0
        return GDALPamRasterBand::SetMetadata(papszMD, pszDomain);
608
609
    /* -------------------------------------------------------------------- */
610
    /*      Set each item individually.                                     */
611
    /* -------------------------------------------------------------------- */
612
0
    CSLDestroy(papszLastMDListValue);
613
0
    papszLastMDListValue = nullptr;
614
0
    m_oCacheMetadataItem.clear();
615
616
0
    if (GetAccess() == GA_ReadOnly)
617
0
    {
618
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
619
0
                 "Unable to set metadata on read-only file.");
620
0
        return CE_Failure;
621
0
    }
622
623
0
    try
624
0
    {
625
0
        for (int iItem = 0; papszMD && papszMD[iItem]; iItem++)
626
0
        {
627
0
            char *pszItemName = nullptr;
628
629
0
            const char *pszItemValue =
630
0
                CPLParseNameValue(papszMD[iItem], &pszItemName);
631
0
            if (pszItemName != nullptr)
632
0
            {
633
0
                poChannel->SetMetadataValue(pszItemName, pszItemValue);
634
0
                CPLFree(pszItemName);
635
0
            }
636
0
        }
637
0
    }
638
0
    catch (const PCIDSKException &ex)
639
0
    {
640
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
641
0
        return CE_Failure;
642
0
    }
643
644
0
    return CE_None;
645
0
}
646
647
/************************************************************************/
648
/*                          SetMetadataItem()                           */
649
/************************************************************************/
650
651
CPLErr PCIDSK2Band::SetMetadataItem(const char *pszName, const char *pszValue,
652
                                    const char *pszDomain)
653
654
0
{
655
    /* -------------------------------------------------------------------- */
656
    /*      PCIDSK only supports metadata in the default domain.            */
657
    /* -------------------------------------------------------------------- */
658
0
    if (pszDomain != nullptr && strlen(pszDomain) > 0)
659
0
        return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
660
661
    /* -------------------------------------------------------------------- */
662
    /*      Set on the file.                                                */
663
    /* -------------------------------------------------------------------- */
664
0
    CSLDestroy(papszLastMDListValue);
665
0
    papszLastMDListValue = nullptr;
666
0
    m_oCacheMetadataItem.clear();
667
668
0
    if (GetAccess() == GA_ReadOnly)
669
0
    {
670
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
671
0
                 "Unable to set metadata on read-only file.");
672
0
        return CE_Failure;
673
0
    }
674
675
0
    try
676
0
    {
677
0
        if (!pszValue)
678
0
            pszValue = "";
679
0
        poChannel->SetMetadataValue(pszName, pszValue);
680
0
    }
681
0
    catch (const PCIDSKException &ex)
682
0
    {
683
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
684
0
        return CE_Failure;
685
0
    }
686
687
0
    return CE_None;
688
0
}
689
690
/************************************************************************/
691
/*                      GetMetadataDomainList()                         */
692
/************************************************************************/
693
694
char **PCIDSK2Band::GetMetadataDomainList()
695
0
{
696
0
    return BuildMetadataDomainList(GDALPamRasterBand::GetMetadataDomainList(),
697
0
                                   TRUE, "", nullptr);
698
0
}
699
700
/************************************************************************/
701
/*                          GetMetadataItem()                           */
702
/************************************************************************/
703
704
const char *PCIDSK2Band::GetMetadataItem(const char *pszName,
705
                                         const char *pszDomain)
706
707
377
{
708
    /* -------------------------------------------------------------------- */
709
    /*      PCIDSK only supports metadata in the default domain.            */
710
    /* -------------------------------------------------------------------- */
711
377
    if (pszDomain != nullptr && strlen(pszDomain) > 0)
712
0
        return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain);
713
714
    /* -------------------------------------------------------------------- */
715
    /*      Try and fetch (use cached value if available)                   */
716
    /* -------------------------------------------------------------------- */
717
377
    auto oIter = m_oCacheMetadataItem.find(pszName);
718
377
    if (oIter != m_oCacheMetadataItem.end())
719
0
    {
720
0
        return oIter->second.empty() ? nullptr : oIter->second.c_str();
721
0
    }
722
723
377
    CPLString osValue;
724
377
    try
725
377
    {
726
377
        osValue = poChannel->GetMetadataValue(pszName);
727
377
    }
728
377
    catch (const PCIDSKException &ex)
729
377
    {
730
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
731
0
        return nullptr;
732
0
    }
733
734
377
    oIter = m_oCacheMetadataItem
735
377
                .insert(std::pair<std::string, std::string>(pszName, osValue))
736
377
                .first;
737
377
    return oIter->second.empty() ? nullptr : oIter->second.c_str();
738
377
}
739
740
/************************************************************************/
741
/*                            GetMetadata()                             */
742
/************************************************************************/
743
744
char **PCIDSK2Band::GetMetadata(const char *pszDomain)
745
746
377
{
747
    /* -------------------------------------------------------------------- */
748
    /*      PCIDSK only supports metadata in the default domain.            */
749
    /* -------------------------------------------------------------------- */
750
377
    if (pszDomain != nullptr && strlen(pszDomain) > 0)
751
0
        return GDALPamRasterBand::GetMetadata(pszDomain);
752
753
    /* -------------------------------------------------------------------- */
754
    /*      If we have a cached result, just use that.                      */
755
    /* -------------------------------------------------------------------- */
756
377
    if (papszLastMDListValue != nullptr)
757
0
        return papszLastMDListValue;
758
759
    /* -------------------------------------------------------------------- */
760
    /*      Fetch and build the list.                                       */
761
    /* -------------------------------------------------------------------- */
762
377
    try
763
377
    {
764
377
        std::vector<std::string> aosKeys = poChannel->GetMetadataKeys();
765
766
387
        for (unsigned int i = 0; i < aosKeys.size(); i++)
767
10
        {
768
10
            if (aosKeys[i].c_str()[0] == '_')
769
10
                continue;
770
771
0
            papszLastMDListValue = CSLSetNameValue(
772
0
                papszLastMDListValue, aosKeys[i].c_str(),
773
0
                poChannel->GetMetadataValue(aosKeys[i]).c_str());
774
0
        }
775
377
    }
776
377
    catch (const PCIDSKException &ex)
777
377
    {
778
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
779
0
        return nullptr;
780
0
    }
781
782
377
    return papszLastMDListValue;
783
377
}
784
785
/************************************************************************/
786
/* ==================================================================== */
787
/*                            PCIDSK2Dataset                            */
788
/* ==================================================================== */
789
/************************************************************************/
790
791
/************************************************************************/
792
/*                           PCIDSK2Dataset()                            */
793
/************************************************************************/
794
795
PCIDSK2Dataset::PCIDSK2Dataset()
796
3.29k
    : papszLastMDListValue(nullptr), poFile(nullptr)
797
3.29k
{
798
3.29k
}
799
800
/************************************************************************/
801
/*                            ~PCIDSK2Dataset()                          */
802
/************************************************************************/
803
804
// FIXME? is an exception can really be thrown in the destructor, then it is
805
// very dangerous !
806
#ifdef _MSC_VER
807
#pragma warning(push)
808
#pragma warning(disable : 4702) /*  unreachable code */
809
#endif
810
PCIDSK2Dataset::~PCIDSK2Dataset()
811
3.29k
{
812
3.29k
    PCIDSK2Dataset::FlushCache(true);
813
814
5.76k
    while (!apoLayers.empty())
815
2.46k
    {
816
2.46k
        delete apoLayers.back();
817
2.46k
        apoLayers.pop_back();
818
2.46k
    }
819
820
3.29k
    if (m_poSRS)
821
31
        m_poSRS->Release();
822
823
3.29k
    try
824
3.29k
    {
825
3.29k
        if (poFile != nullptr)
826
3.29k
            delete poFile;
827
3.29k
    }
828
829
    /* -------------------------------------------------------------------- */
830
    /*      Trap exceptions.                                                */
831
    /* -------------------------------------------------------------------- */
832
3.29k
    catch (const PCIDSKException &ex)
833
3.29k
    {
834
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
835
0
    }
836
3.29k
    catch (...)
837
3.29k
    {
838
0
        CPLError(CE_Failure, CPLE_AppDefined,
839
0
                 "PCIDSK SDK Failure in Close(), unexpected exception.");
840
0
    }
841
842
3.29k
    CSLDestroy(papszLastMDListValue);
843
0
}
844
#ifdef _MSC_VER
845
#pragma warning(pop)
846
#endif
847
848
/************************************************************************/
849
/*                            GetFileList()                             */
850
/************************************************************************/
851
852
char **PCIDSK2Dataset::GetFileList()
853
854
879
{
855
879
    char **papszFileList = GDALPamDataset::GetFileList();
856
879
    CPLString osBaseDir = CPLGetPathSafe(GetDescription());
857
858
879
    try
859
879
    {
860
306k
        for (int nChan = 1; nChan <= poFile->GetChannels(); nChan++)
861
305k
        {
862
305k
            PCIDSKChannel *poChannel = poFile->GetChannel(nChan);
863
305k
            CPLString osChanFilename;
864
305k
            uint64 image_offset, pixel_offset, line_offset;
865
305k
            bool little_endian;
866
867
305k
            poChannel->GetChanInfo(osChanFilename, image_offset, pixel_offset,
868
305k
                                   line_offset, little_endian);
869
870
305k
            if (osChanFilename != "")
871
302k
            {
872
302k
                papszFileList = CSLAddString(
873
302k
                    papszFileList,
874
302k
                    CPLProjectRelativeFilenameSafe(osBaseDir, osChanFilename)
875
302k
                        .c_str());
876
302k
            }
877
305k
        }
878
879
879
        return papszFileList;
880
879
    }
881
879
    catch (const PCIDSKException &ex)
882
879
    {
883
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
884
0
        return papszFileList;
885
0
    }
886
879
}
887
888
/************************************************************************/
889
/*                             ProcessRPC()                             */
890
/************************************************************************/
891
892
void PCIDSK2Dataset::ProcessRPC()
893
894
3.23k
{
895
    /* -------------------------------------------------------------------- */
896
    /*      Search all BIN segments looking for an RPC segment.             */
897
    /* -------------------------------------------------------------------- */
898
3.23k
    PCIDSKSegment *poSeg = poFile->GetSegment(SEG_BIN, "");
899
3.23k
    PCIDSKRPCSegment *poRPCSeg = nullptr;
900
901
4.58k
    while (poSeg != nullptr &&
902
4.58k
           (poRPCSeg = dynamic_cast<PCIDSKRPCSegment *>(poSeg)) == nullptr)
903
904
1.35k
    {
905
1.35k
        poSeg = poFile->GetSegment(SEG_BIN, "", poSeg->GetSegmentNumber());
906
1.35k
    }
907
908
3.23k
    if (poRPCSeg == nullptr)
909
3.19k
        return;
910
911
    /* -------------------------------------------------------------------- */
912
    /*      Turn RPC segment into GDAL RFC 22 style metadata.               */
913
    /* -------------------------------------------------------------------- */
914
32
    try
915
32
    {
916
32
        CPLString osValue;
917
32
        double dfLineOffset, dfLineScale, dfSampOffset, dfSampScale;
918
32
        double dfLatOffset, dfLatScale, dfLongOffset, dfLongScale,
919
32
            dfHeightOffset, dfHeightScale;
920
921
32
        poRPCSeg->GetRPCTranslationCoeffs(
922
32
            dfLongOffset, dfLongScale, dfLatOffset, dfLatScale, dfHeightOffset,
923
32
            dfHeightScale, dfSampOffset, dfSampScale, dfLineOffset,
924
32
            dfLineScale);
925
926
32
        osValue.Printf("%.16g", dfLineOffset);
927
32
        GDALPamDataset::SetMetadataItem("LINE_OFF", osValue, "RPC");
928
929
32
        osValue.Printf("%.16g", dfLineScale);
930
32
        GDALPamDataset::SetMetadataItem("LINE_SCALE", osValue, "RPC");
931
932
32
        osValue.Printf("%.16g", dfSampOffset);
933
32
        GDALPamDataset::SetMetadataItem("SAMP_OFF", osValue, "RPC");
934
935
32
        osValue.Printf("%.16g", dfSampScale);
936
32
        GDALPamDataset::SetMetadataItem("SAMP_SCALE", osValue, "RPC");
937
938
32
        osValue.Printf("%.16g", dfLongOffset);
939
32
        GDALPamDataset::SetMetadataItem("LONG_OFF", osValue, "RPC");
940
941
32
        osValue.Printf("%.16g", dfLongScale);
942
32
        GDALPamDataset::SetMetadataItem("LONG_SCALE", osValue, "RPC");
943
944
32
        osValue.Printf("%.16g", dfLatOffset);
945
32
        GDALPamDataset::SetMetadataItem("LAT_OFF", osValue, "RPC");
946
947
32
        osValue.Printf("%.16g", dfLatScale);
948
32
        GDALPamDataset::SetMetadataItem("LAT_SCALE", osValue, "RPC");
949
950
32
        osValue.Printf("%.16g", dfHeightOffset);
951
32
        GDALPamDataset::SetMetadataItem("HEIGHT_OFF", osValue, "RPC");
952
953
32
        osValue.Printf("%.16g", dfHeightScale);
954
32
        GDALPamDataset::SetMetadataItem("HEIGHT_SCALE", osValue, "RPC");
955
956
32
        if (poRPCSeg->GetXNumerator().size() != 20 ||
957
32
            poRPCSeg->GetXDenominator().size() != 20 ||
958
32
            poRPCSeg->GetYNumerator().size() != 20 ||
959
32
            poRPCSeg->GetYDenominator().size() != 20)
960
0
        {
961
0
            GDALPamDataset::SetMetadata(nullptr, "RPC");
962
0
            CPLError(CE_Failure, CPLE_AppDefined,
963
0
                     "Did not get 20 values in the RPC coefficients lists.");
964
0
            return;
965
0
        }
966
967
32
        std::vector<double> adfCoef = poRPCSeg->GetYNumerator();
968
32
        CPLString osCoefList = "";
969
32
        for (int i = 0; i < 20; i++)
970
0
        {
971
0
            osValue.Printf("%.16g ", adfCoef[i]);
972
0
            osCoefList += osValue;
973
0
        }
974
32
        GDALPamDataset::SetMetadataItem("LINE_NUM_COEFF", osCoefList, "RPC");
975
976
32
        adfCoef = poRPCSeg->GetYDenominator();
977
32
        osCoefList = "";
978
32
        for (int i = 0; i < 20; i++)
979
0
        {
980
0
            osValue.Printf("%.16g ", adfCoef[i]);
981
0
            osCoefList += osValue;
982
0
        }
983
32
        GDALPamDataset::SetMetadataItem("LINE_DEN_COEFF", osCoefList, "RPC");
984
985
32
        adfCoef = poRPCSeg->GetXNumerator();
986
32
        osCoefList = "";
987
32
        for (int i = 0; i < 20; i++)
988
0
        {
989
0
            osValue.Printf("%.16g ", adfCoef[i]);
990
0
            osCoefList += osValue;
991
0
        }
992
32
        GDALPamDataset::SetMetadataItem("SAMP_NUM_COEFF", osCoefList, "RPC");
993
994
32
        adfCoef = poRPCSeg->GetXDenominator();
995
32
        osCoefList = "";
996
32
        for (int i = 0; i < 20; i++)
997
0
        {
998
0
            osValue.Printf("%.16g ", adfCoef[i]);
999
0
            osCoefList += osValue;
1000
0
        }
1001
32
        GDALPamDataset::SetMetadataItem("SAMP_DEN_COEFF", osCoefList, "RPC");
1002
32
    }
1003
32
    catch (const PCIDSKException &ex)
1004
32
    {
1005
0
        GDALPamDataset::SetMetadata(nullptr, "RPC");
1006
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1007
0
    }
1008
32
}
1009
1010
/************************************************************************/
1011
/*                             FlushCache()                             */
1012
/************************************************************************/
1013
1014
CPLErr PCIDSK2Dataset::FlushCache(bool bAtClosing)
1015
1016
3.36k
{
1017
3.36k
    CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
1018
1019
3.36k
    if (poFile)
1020
3.36k
    {
1021
3.36k
        try
1022
3.36k
        {
1023
3.36k
            poFile->Synchronize();
1024
3.36k
        }
1025
3.36k
        catch (const PCIDSKException &ex)
1026
3.36k
        {
1027
0
            eErr = CE_Failure;
1028
0
            CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1029
0
        }
1030
3.36k
    }
1031
3.36k
    return eErr;
1032
3.36k
}
1033
1034
/************************************************************************/
1035
/*                            SetMetadata()                             */
1036
/************************************************************************/
1037
1038
CPLErr PCIDSK2Dataset::SetMetadata(char **papszMD, const char *pszDomain)
1039
1040
0
{
1041
    /* -------------------------------------------------------------------- */
1042
    /*      PCIDSK only supports metadata in the default domain.            */
1043
    /* -------------------------------------------------------------------- */
1044
0
    if (pszDomain != nullptr && strlen(pszDomain) > 0)
1045
0
        return GDALPamDataset::SetMetadata(papszMD, pszDomain);
1046
1047
    /* -------------------------------------------------------------------- */
1048
    /*      Set each item individually.                                     */
1049
    /* -------------------------------------------------------------------- */
1050
0
    CSLDestroy(papszLastMDListValue);
1051
0
    papszLastMDListValue = nullptr;
1052
0
    m_oCacheMetadataItem.clear();
1053
1054
0
    if (GetAccess() == GA_ReadOnly)
1055
0
    {
1056
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
1057
0
                 "Unable to set metadata on read-only file.");
1058
0
        return CE_Failure;
1059
0
    }
1060
1061
0
    try
1062
0
    {
1063
0
        for (int iItem = 0; papszMD && papszMD[iItem]; iItem++)
1064
0
        {
1065
0
            char *pszItemName = nullptr;
1066
0
            const char *pszItemValue =
1067
0
                CPLParseNameValue(papszMD[iItem], &pszItemName);
1068
0
            if (pszItemName != nullptr)
1069
0
            {
1070
0
                poFile->SetMetadataValue(pszItemName, pszItemValue);
1071
0
                CPLFree(pszItemName);
1072
0
            }
1073
0
        }
1074
0
    }
1075
0
    catch (const PCIDSKException &ex)
1076
0
    {
1077
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1078
0
        return CE_Failure;
1079
0
    }
1080
1081
0
    return CE_None;
1082
0
}
1083
1084
/************************************************************************/
1085
/*                          SetMetadataItem()                           */
1086
/************************************************************************/
1087
1088
CPLErr PCIDSK2Dataset::SetMetadataItem(const char *pszName,
1089
                                       const char *pszValue,
1090
                                       const char *pszDomain)
1091
1092
686
{
1093
    /* -------------------------------------------------------------------- */
1094
    /*      PCIDSK only supports metadata in the default domain.            */
1095
    /* -------------------------------------------------------------------- */
1096
686
    if (pszDomain != nullptr && strlen(pszDomain) > 0)
1097
645
        return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1098
1099
    /* -------------------------------------------------------------------- */
1100
    /*      Set on the file.                                                */
1101
    /* -------------------------------------------------------------------- */
1102
41
    CSLDestroy(papszLastMDListValue);
1103
41
    papszLastMDListValue = nullptr;
1104
41
    m_oCacheMetadataItem.clear();
1105
1106
41
    if (GetAccess() == GA_ReadOnly)
1107
0
    {
1108
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
1109
0
                 "Unable to set metadata on read-only file.");
1110
0
        return CE_Failure;
1111
0
    }
1112
1113
41
    try
1114
41
    {
1115
41
        poFile->SetMetadataValue(pszName, pszValue);
1116
41
    }
1117
41
    catch (const PCIDSKException &ex)
1118
41
    {
1119
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1120
0
        return CE_Failure;
1121
0
    }
1122
1123
41
    return CE_None;
1124
41
}
1125
1126
/************************************************************************/
1127
/*                      GetMetadataDomainList()                         */
1128
/************************************************************************/
1129
1130
char **PCIDSK2Dataset::GetMetadataDomainList()
1131
0
{
1132
0
    return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
1133
0
                                   TRUE, "", nullptr);
1134
0
}
1135
1136
/************************************************************************/
1137
/*                          GetMetadataItem()                           */
1138
/************************************************************************/
1139
1140
const char *PCIDSK2Dataset::GetMetadataItem(const char *pszName,
1141
                                            const char *pszDomain)
1142
1143
4.75k
{
1144
    /* -------------------------------------------------------------------- */
1145
    /*      PCIDSK only supports metadata in the default domain.            */
1146
    /* -------------------------------------------------------------------- */
1147
4.75k
    if (pszDomain != nullptr && strlen(pszDomain) > 0)
1148
2.85k
        return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
1149
1150
    /* -------------------------------------------------------------------- */
1151
    /*      Try and fetch (use cached value if available)                   */
1152
    /* -------------------------------------------------------------------- */
1153
1.90k
    auto oIter = m_oCacheMetadataItem.find(pszName);
1154
1.90k
    if (oIter != m_oCacheMetadataItem.end())
1155
1.03k
    {
1156
1.03k
        return oIter->second.empty() ? nullptr : oIter->second.c_str();
1157
1.03k
    }
1158
1159
869
    CPLString osValue;
1160
869
    try
1161
869
    {
1162
869
        osValue = poFile->GetMetadataValue(pszName);
1163
869
    }
1164
869
    catch (const PCIDSKException &ex)
1165
869
    {
1166
1
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1167
1
        return nullptr;
1168
1
    }
1169
1170
868
    oIter = m_oCacheMetadataItem
1171
868
                .insert(std::pair<std::string, std::string>(pszName, osValue))
1172
868
                .first;
1173
868
    return oIter->second.empty() ? nullptr : oIter->second.c_str();
1174
869
}
1175
1176
/************************************************************************/
1177
/*                            GetMetadata()                             */
1178
/************************************************************************/
1179
1180
char **PCIDSK2Dataset::GetMetadata(const char *pszDomain)
1181
1182
423
{
1183
    /* -------------------------------------------------------------------- */
1184
    /*      PCIDSK only supports metadata in the default domain.            */
1185
    /* -------------------------------------------------------------------- */
1186
423
    if (pszDomain != nullptr && strlen(pszDomain) > 0)
1187
0
        return GDALPamDataset::GetMetadata(pszDomain);
1188
1189
    /* -------------------------------------------------------------------- */
1190
    /*      If we have a cached result, just use that.                      */
1191
    /* -------------------------------------------------------------------- */
1192
423
    if (papszLastMDListValue != nullptr)
1193
0
        return papszLastMDListValue;
1194
1195
    /* -------------------------------------------------------------------- */
1196
    /*      Fetch and build the list.                                       */
1197
    /* -------------------------------------------------------------------- */
1198
423
    try
1199
423
    {
1200
423
        std::vector<std::string> aosKeys = poFile->GetMetadataKeys();
1201
1202
453
        for (unsigned int i = 0; i < aosKeys.size(); i++)
1203
30
        {
1204
30
            if (aosKeys[i].c_str()[0] == '_')
1205
7
                continue;
1206
1207
23
            papszLastMDListValue =
1208
23
                CSLSetNameValue(papszLastMDListValue, aosKeys[i].c_str(),
1209
23
                                poFile->GetMetadataValue(aosKeys[i]).c_str());
1210
23
        }
1211
423
    }
1212
423
    catch (const PCIDSKException &ex)
1213
423
    {
1214
1
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1215
1
        return nullptr;
1216
1
    }
1217
1218
422
    return papszLastMDListValue;
1219
423
}
1220
1221
/************************************************************************/
1222
/*                          SetGeoTransform()                           */
1223
/************************************************************************/
1224
1225
CPLErr PCIDSK2Dataset::SetGeoTransform(double *padfTransform)
1226
0
{
1227
0
    PCIDSKGeoref *poGeoref = nullptr;
1228
0
    try
1229
0
    {
1230
0
        PCIDSKSegment *poGeoSeg = poFile->GetSegment(1);
1231
0
        poGeoref = dynamic_cast<PCIDSKGeoref *>(poGeoSeg);
1232
0
    }
1233
0
    catch (const PCIDSKException &)
1234
0
    {
1235
        // I should really check whether this is an expected issue.
1236
0
    }
1237
1238
0
    if (poGeoref == nullptr)
1239
0
        return GDALPamDataset::SetGeoTransform(padfTransform);
1240
1241
0
    if (GetAccess() == GA_ReadOnly)
1242
0
    {
1243
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
1244
0
                 "Unable to set GeoTransform on read-only file.");
1245
0
        return CE_Failure;
1246
0
    }
1247
1248
0
    try
1249
0
    {
1250
0
        poGeoref->WriteSimple(poGeoref->GetGeosys(), padfTransform[0],
1251
0
                              padfTransform[1], padfTransform[2],
1252
0
                              padfTransform[3], padfTransform[4],
1253
0
                              padfTransform[5]);
1254
0
    }
1255
0
    catch (const PCIDSKException &ex)
1256
0
    {
1257
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1258
0
        return CE_Failure;
1259
0
    }
1260
1261
0
    return CE_None;
1262
0
}
1263
1264
/************************************************************************/
1265
/*                          GetGeoTransform()                           */
1266
/************************************************************************/
1267
1268
CPLErr PCIDSK2Dataset::GetGeoTransform(double *padfTransform)
1269
426
{
1270
426
    PCIDSKGeoref *poGeoref = nullptr;
1271
426
    try
1272
426
    {
1273
426
        PCIDSKSegment *poGeoSeg = poFile->GetSegment(1);
1274
426
        poGeoref = dynamic_cast<PCIDSKGeoref *>(poGeoSeg);
1275
426
    }
1276
426
    catch (const PCIDSKException &)
1277
426
    {
1278
        // I should really check whether this is an expected issue.
1279
0
    }
1280
1281
426
    if (poGeoref != nullptr)
1282
34
    {
1283
34
        try
1284
34
        {
1285
34
            poGeoref->GetTransform(padfTransform[0], padfTransform[1],
1286
34
                                   padfTransform[2], padfTransform[3],
1287
34
                                   padfTransform[4], padfTransform[5]);
1288
34
        }
1289
34
        catch (const PCIDSKException &ex)
1290
34
        {
1291
2
            CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1292
2
            return CE_Failure;
1293
2
        }
1294
1295
        // If we got anything non-default return it.
1296
32
        if (padfTransform[0] != 0.0 || padfTransform[1] != 1.0 ||
1297
32
            padfTransform[2] != 0.0 || padfTransform[3] != 0.0 ||
1298
32
            padfTransform[4] != 0.0 || padfTransform[5] != 1.0)
1299
31
            return CE_None;
1300
32
    }
1301
1302
    /* -------------------------------------------------------------------- */
1303
    /*      Check for worldfile if we have no other georeferencing.         */
1304
    /* -------------------------------------------------------------------- */
1305
393
    if (GDALReadWorldFile(GetDescription(), "pxw", padfTransform))
1306
0
        return CE_None;
1307
1308
393
    return GDALPamDataset::GetGeoTransform(padfTransform);
1309
393
}
1310
1311
/************************************************************************/
1312
/*                           SetSpatialRef()                            */
1313
/************************************************************************/
1314
1315
CPLErr PCIDSK2Dataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1316
1317
0
{
1318
0
    PCIDSKGeoref *poGeoref = nullptr;
1319
1320
0
    try
1321
0
    {
1322
0
        PCIDSKSegment *poGeoSeg = poFile->GetSegment(1);
1323
0
        poGeoref = dynamic_cast<PCIDSKGeoref *>(poGeoSeg);
1324
0
    }
1325
0
    catch (const PCIDSKException &)
1326
0
    {
1327
        // I should really check whether this is an expected issue.
1328
0
    }
1329
1330
0
    if (poGeoref == nullptr)
1331
0
    {
1332
0
        return GDALPamDataset::SetSpatialRef(poSRS);
1333
0
    }
1334
1335
0
    char *pszGeosys = nullptr;
1336
0
    char *pszUnits = nullptr;
1337
0
    double *padfPrjParams = nullptr;
1338
1339
0
    if (poSRS == nullptr || poSRS->exportToPCI(&pszGeosys, &pszUnits,
1340
0
                                               &padfPrjParams) != OGRERR_NONE)
1341
0
    {
1342
0
        return GDALPamDataset::SetSpatialRef(poSRS);
1343
0
    }
1344
1345
0
    if (GetAccess() == GA_ReadOnly)
1346
0
    {
1347
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
1348
0
                 "Unable to set projection on read-only file.");
1349
0
        CPLFree(pszGeosys);
1350
0
        CPLFree(pszUnits);
1351
0
        CPLFree(padfPrjParams);
1352
0
        return CE_Failure;
1353
0
    }
1354
1355
0
    try
1356
0
    {
1357
0
        double adfGT[6];
1358
0
        poGeoref->GetTransform(adfGT[0], adfGT[1], adfGT[2], adfGT[3], adfGT[4],
1359
0
                               adfGT[5]);
1360
1361
0
        poGeoref->WriteSimple(pszGeosys, adfGT[0], adfGT[1], adfGT[2], adfGT[3],
1362
0
                              adfGT[4], adfGT[5]);
1363
1364
0
        std::vector<double> adfPCIParameters;
1365
0
        for (unsigned int i = 0; i < 17; i++)
1366
0
            adfPCIParameters.push_back(padfPrjParams[i]);
1367
1368
0
        if (STARTS_WITH_CI(pszUnits, "FOOT"))
1369
0
            adfPCIParameters.push_back(
1370
0
                static_cast<double>(static_cast<int>(PCIDSK::UNIT_US_FOOT)));
1371
0
        else if (EQUALN(pszUnits, "INTL FOOT", 9))
1372
0
            adfPCIParameters.push_back(
1373
0
                static_cast<double>(static_cast<int>(PCIDSK::UNIT_INTL_FOOT)));
1374
0
        else if (EQUALN(pszUnits, "DEGREE", 6))
1375
0
            adfPCIParameters.push_back(
1376
0
                static_cast<double>(static_cast<int>(PCIDSK::UNIT_DEGREE)));
1377
0
        else
1378
0
            adfPCIParameters.push_back(
1379
0
                static_cast<double>(static_cast<int>(PCIDSK::UNIT_METER)));
1380
1381
0
        poGeoref->WriteParameters(adfPCIParameters);
1382
0
    }
1383
0
    catch (const PCIDSKException &ex)
1384
0
    {
1385
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1386
0
        return CE_Failure;
1387
0
    }
1388
1389
0
    CPLFree(pszGeosys);
1390
0
    CPLFree(pszUnits);
1391
0
    CPLFree(padfPrjParams);
1392
1393
0
    return CE_None;
1394
0
}
1395
1396
/************************************************************************/
1397
/*                          GetSpatialRef()                             */
1398
/************************************************************************/
1399
1400
const OGRSpatialReference *PCIDSK2Dataset::GetSpatialRef() const
1401
426
{
1402
426
    if (m_poSRS)
1403
0
        return m_poSRS;
1404
1405
426
    PCIDSKGeoref *poGeoref = nullptr;
1406
1407
426
    try
1408
426
    {
1409
426
        PCIDSKSegment *poGeoSeg = poFile->GetSegment(1);
1410
426
        poGeoref = dynamic_cast<PCIDSKGeoref *>(poGeoSeg);
1411
426
    }
1412
426
    catch (const PCIDSKException &)
1413
426
    {
1414
        // I should really check whether this is an expected issue.
1415
0
    }
1416
1417
426
    if (poGeoref == nullptr)
1418
392
    {
1419
392
        return GDALPamDataset::GetSpatialRef();
1420
392
    }
1421
1422
34
    CPLString osGeosys;
1423
34
    const char *pszUnits = nullptr;
1424
1425
34
    std::vector<double> adfParameters;
1426
34
    adfParameters.resize(18);
1427
1428
34
    try
1429
34
    {
1430
34
        osGeosys = poGeoref->GetGeosys();
1431
34
        adfParameters = poGeoref->GetParameters();
1432
34
        const UnitCode code =
1433
34
            static_cast<UnitCode>(static_cast<int>(adfParameters[16]));
1434
1435
34
        if (code == PCIDSK::UNIT_DEGREE)
1436
0
            pszUnits = "DEGREE";
1437
34
        else if (code == PCIDSK::UNIT_METER)
1438
0
            pszUnits = "METER";
1439
34
        else if (code == PCIDSK::UNIT_US_FOOT)
1440
0
            pszUnits = "FOOT";
1441
34
        else if (code == PCIDSK::UNIT_INTL_FOOT)
1442
0
            pszUnits = "INTL FOOT";
1443
34
    }
1444
34
    catch (const PCIDSKException &ex)
1445
34
    {
1446
2
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1447
2
    }
1448
1449
34
    OGRSpatialReference oSRS;
1450
34
    oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1451
34
    if (oSRS.importFromPCI(osGeosys, pszUnits, &(adfParameters[0])) ==
1452
34
        OGRERR_NONE)
1453
31
    {
1454
31
        m_poSRS = oSRS.Clone();
1455
31
        return m_poSRS;
1456
31
    }
1457
3
    else
1458
3
    {
1459
3
        return GDALPamDataset::GetSpatialRef();
1460
3
    }
1461
34
}
1462
1463
/************************************************************************/
1464
/*                          IBuildOverviews()                           */
1465
/************************************************************************/
1466
1467
CPLErr PCIDSK2Dataset::IBuildOverviews(
1468
    const char *pszResampling, int nOverviews, const int *panOverviewList,
1469
    int nListBands, const int *panBandList, GDALProgressFunc pfnProgress,
1470
    void *pProgressData, CSLConstList papszOptions)
1471
1472
0
{
1473
0
    PCIDSK2Band *poBand =
1474
0
        reinterpret_cast<PCIDSK2Band *>(GetRasterBand(panBandList[0]));
1475
1476
    /* -------------------------------------------------------------------- */
1477
    /*      If RRD overviews requested, then invoke generic handling.       */
1478
    /* -------------------------------------------------------------------- */
1479
0
    bool bUseGenericHandling = false;
1480
1481
0
    if (CPLTestBool(CPLGetConfigOption("USE_RRD", "NO")))
1482
0
    {
1483
0
        bUseGenericHandling = true;
1484
0
    }
1485
1486
    /* -------------------------------------------------------------------- */
1487
    /*      If we don't have read access, then create the overviews         */
1488
    /*      externally.                                                     */
1489
    /* -------------------------------------------------------------------- */
1490
0
    if (GetAccess() != GA_Update)
1491
0
    {
1492
0
        CPLDebug("PCIDSK", "File open for read-only accessing, "
1493
0
                           "creating overviews externally.");
1494
1495
0
        bUseGenericHandling = true;
1496
0
    }
1497
1498
0
    if (bUseGenericHandling)
1499
0
    {
1500
0
        if (poBand->GetOverviewCount() != 0)
1501
0
        {
1502
0
            CPLError(CE_Failure, CPLE_NotSupported,
1503
0
                     "Cannot add external overviews when there are already "
1504
0
                     "internal overviews");
1505
0
            return CE_Failure;
1506
0
        }
1507
1508
0
        return GDALDataset::IBuildOverviews(
1509
0
            pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
1510
0
            pfnProgress, pProgressData, papszOptions);
1511
0
    }
1512
1513
0
    if (nListBands == 0)
1514
0
        return CE_None;
1515
1516
    /* -------------------------------------------------------------------- */
1517
    /*      Currently no support for clearing overviews.                    */
1518
    /* -------------------------------------------------------------------- */
1519
0
    if (nOverviews == 0)
1520
0
    {
1521
0
        CPLError(CE_Failure, CPLE_AppDefined,
1522
0
                 "PCIDSK2 driver does not currently support clearing existing "
1523
0
                 "overviews. ");
1524
0
        return CE_Failure;
1525
0
    }
1526
1527
    /* -------------------------------------------------------------------- */
1528
    /*      Establish which of the overview levels we already have, and     */
1529
    /*      which are new.  We assume that band 1 of the file is            */
1530
    /*      representative.                                                 */
1531
    /* -------------------------------------------------------------------- */
1532
1533
0
    int nNewOverviews = 0;
1534
0
    int *panNewOverviewList =
1535
0
        reinterpret_cast<int *>(CPLCalloc(sizeof(int), nOverviews));
1536
0
    std::vector<bool> abFoundOverviewFactor(nOverviews);
1537
0
    for (int i = 0; i < nOverviews && poBand != nullptr; i++)
1538
0
    {
1539
0
        for (int j = 0; j < poBand->GetOverviewCount(); j++)
1540
0
        {
1541
0
            GDALRasterBand *poOverview = poBand->GetOverview(j);
1542
1543
0
            int nOvFactor =
1544
0
                GDALComputeOvFactor(poOverview->GetXSize(), poBand->GetXSize(),
1545
0
                                    poOverview->GetYSize(), poBand->GetYSize());
1546
1547
0
            if (nOvFactor == panOverviewList[i] ||
1548
0
                nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
1549
0
                                                poBand->GetXSize(),
1550
0
                                                poBand->GetYSize()))
1551
0
                abFoundOverviewFactor[i] = true;
1552
0
        }
1553
1554
0
        if (!abFoundOverviewFactor[i])
1555
0
            panNewOverviewList[nNewOverviews++] = panOverviewList[i];
1556
0
    }
1557
1558
    /* -------------------------------------------------------------------- */
1559
    /*      Create the overviews that are missing.                          */
1560
    /* -------------------------------------------------------------------- */
1561
0
    for (int i = 0; i < nNewOverviews; i++)
1562
0
    {
1563
0
        try
1564
0
        {
1565
            // conveniently our resampling values mostly match PCIDSK.
1566
0
            poFile->CreateOverviews(nListBands, panBandList,
1567
0
                                    panNewOverviewList[i], pszResampling);
1568
0
        }
1569
0
        catch (const PCIDSKException &ex)
1570
0
        {
1571
0
            CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1572
0
            CPLFree(panNewOverviewList);
1573
0
            return CE_Failure;
1574
0
        }
1575
0
    }
1576
1577
0
    CPLFree(panNewOverviewList);
1578
0
    panNewOverviewList = nullptr;
1579
1580
0
    for (int iBand = 0; iBand < nListBands; iBand++)
1581
0
    {
1582
0
        poBand =
1583
0
            reinterpret_cast<PCIDSK2Band *>(GetRasterBand(panBandList[iBand]));
1584
0
        reinterpret_cast<PCIDSK2Band *>(poBand)->RefreshOverviewList();
1585
0
    }
1586
1587
    /* -------------------------------------------------------------------- */
1588
    /*      Actually generate the overview imagery.                         */
1589
    /* -------------------------------------------------------------------- */
1590
0
    CPLErr eErr = CE_None;
1591
0
    std::vector<int> anRegenLevels;
1592
1593
0
    GDALRasterBand **papoOverviewBands = reinterpret_cast<GDALRasterBand **>(
1594
0
        CPLCalloc(sizeof(void *), nOverviews));
1595
1596
0
    for (int iBand = 0; iBand < nListBands && eErr == CE_None; iBand++)
1597
0
    {
1598
0
        nNewOverviews = 0;
1599
1600
0
        poBand =
1601
0
            reinterpret_cast<PCIDSK2Band *>(GetRasterBand(panBandList[iBand]));
1602
1603
0
        for (int i = 0; i < nOverviews && poBand != nullptr; i++)
1604
0
        {
1605
0
            for (int j = 0; j < poBand->GetOverviewCount(); j++)
1606
0
            {
1607
0
                GDALRasterBand *poOverview = poBand->GetOverview(j);
1608
1609
0
                int nOvFactor = GDALComputeOvFactor(
1610
0
                    poOverview->GetXSize(), poBand->GetXSize(),
1611
0
                    poOverview->GetYSize(), poBand->GetYSize());
1612
1613
0
                if (nOvFactor == panOverviewList[i] ||
1614
0
                    nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
1615
0
                                                    poBand->GetXSize(),
1616
0
                                                    poBand->GetYSize()))
1617
0
                {
1618
0
                    papoOverviewBands[nNewOverviews++] = poOverview;
1619
0
                    anRegenLevels.push_back(j);
1620
0
                    break;
1621
0
                }
1622
0
            }
1623
0
        }
1624
1625
0
        if (nNewOverviews > 0)
1626
0
        {
1627
0
            eErr = GDALRegenerateOverviewsEx(
1628
0
                (GDALRasterBandH)poBand, nNewOverviews,
1629
0
                reinterpret_cast<GDALRasterBandH *>(papoOverviewBands),
1630
0
                pszResampling, pfnProgress, pProgressData, papszOptions);
1631
1632
            // Mark the regenerated overviews as valid.
1633
0
            for (int i = 0; i < static_cast<int>(anRegenLevels.size()); i++)
1634
0
                poBand->poChannel->SetOverviewValidity(anRegenLevels[i], true);
1635
0
        }
1636
0
    }
1637
1638
0
    CPLFree(papoOverviewBands);
1639
1640
0
    return eErr;
1641
0
}
1642
1643
/************************************************************************/
1644
/*                         PCIDSKTypeToGDAL()                           */
1645
/************************************************************************/
1646
1647
GDALDataType PCIDSK2Dataset::PCIDSKTypeToGDAL(eChanType eType)
1648
210k
{
1649
210k
    switch (eType)
1650
210k
    {
1651
15.0k
        case CHN_8U:
1652
15.0k
            return GDT_Byte;
1653
1654
12
        case CHN_16U:
1655
12
            return GDT_UInt16;
1656
1657
2
        case CHN_16S:
1658
2
            return GDT_Int16;
1659
1660
156
        case CHN_32R:
1661
156
            return GDT_Float32;
1662
1663
22
        case CHN_BIT:
1664
22
            return GDT_Byte;
1665
1666
0
        case CHN_C16U:
1667
0
            return GDT_CInt16;
1668
1669
16
        case CHN_C16S:
1670
16
            return GDT_CInt16;
1671
1672
14
        case CHN_C32R:
1673
14
            return GDT_CFloat32;
1674
1675
195k
        default:
1676
195k
            return GDT_Unknown;
1677
210k
    }
1678
210k
}
1679
1680
/************************************************************************/
1681
/*                                Open()                                */
1682
/************************************************************************/
1683
1684
GDALDataset *PCIDSK2Dataset::Open(GDALOpenInfo *poOpenInfo)
1685
27.9k
{
1686
27.9k
    if (!PCIDSKDriverIdentify(poOpenInfo))
1687
0
        return nullptr;
1688
1689
    /* -------------------------------------------------------------------- */
1690
    /*      Try opening the file.                                           */
1691
    /* -------------------------------------------------------------------- */
1692
27.9k
    PCIDSKFile *poFile = nullptr;
1693
27.9k
    const int nMaxBandCount =
1694
27.9k
        atoi(CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536"));
1695
27.9k
    try
1696
27.9k
    {
1697
27.9k
        poFile = PCIDSK::Open(poOpenInfo->pszFilename,
1698
27.9k
                              poOpenInfo->eAccess == GA_ReadOnly ? "r" : "r+",
1699
27.9k
                              PCIDSK2GetInterfaces(), nMaxBandCount);
1700
27.9k
        if (poFile == nullptr)
1701
0
        {
1702
0
            CPLError(CE_Failure, CPLE_OpenFailed,
1703
0
                     "Failed to re-open %s within PCIDSK driver.\n",
1704
0
                     poOpenInfo->pszFilename);
1705
0
            return nullptr;
1706
0
        }
1707
1708
27.9k
        const bool bValidRasterDimensions =
1709
27.9k
            poFile->GetWidth() && poFile->GetHeight();
1710
27.9k
        if (!bValidRasterDimensions &&
1711
27.9k
            (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
1712
27.9k
            (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0)
1713
214
        {
1714
214
            delete poFile;
1715
214
            return nullptr;
1716
214
        }
1717
1718
        /* Check if this is a vector-only PCIDSK file and that we are */
1719
        /* opened in raster-only mode */
1720
27.7k
        if (poOpenInfo->eAccess == GA_ReadOnly &&
1721
27.7k
            (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
1722
27.7k
            (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0 &&
1723
27.7k
            poFile->GetChannels() == 0 &&
1724
27.7k
            poFile->GetSegment(PCIDSK::SEG_VEC, "") != nullptr)
1725
0
        {
1726
0
            CPLDebug("PCIDSK",
1727
0
                     "This is a vector-only PCIDSK dataset, "
1728
0
                     "but it has been opened in read-only in raster-only mode");
1729
0
            delete poFile;
1730
0
            return nullptr;
1731
0
        }
1732
        /* Reverse test */
1733
27.7k
        if (poOpenInfo->eAccess == GA_ReadOnly &&
1734
27.7k
            (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
1735
27.7k
            (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
1736
27.7k
            poFile->GetChannels() != 0 &&
1737
27.7k
            poFile->GetSegment(PCIDSK::SEG_VEC, "") == nullptr)
1738
83
        {
1739
83
            CPLDebug("PCIDSK",
1740
83
                     "This is a raster-only PCIDSK dataset, "
1741
83
                     "but it has been opened in read-only in vector-only mode");
1742
83
            delete poFile;
1743
83
            return nullptr;
1744
83
        }
1745
1746
27.6k
        return LLOpen(poOpenInfo->pszFilename, poFile, poOpenInfo->eAccess,
1747
27.6k
                      poOpenInfo->GetSiblingFiles());
1748
27.7k
    }
1749
    /* -------------------------------------------------------------------- */
1750
    /*      Trap exceptions.                                                */
1751
    /* -------------------------------------------------------------------- */
1752
27.9k
    catch (const PCIDSKException &ex)
1753
27.9k
    {
1754
24.4k
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1755
24.4k
        delete poFile;
1756
24.4k
        return nullptr;
1757
24.4k
    }
1758
27.9k
    catch (...)
1759
27.9k
    {
1760
0
        CPLError(CE_Failure, CPLE_AppDefined,
1761
0
                 "PCIDSK::Create() failed, unexpected exception.");
1762
0
        delete poFile;
1763
0
        return nullptr;
1764
0
    }
1765
27.9k
}
1766
1767
/************************************************************************/
1768
/*                               LLOpen()                               */
1769
/*                                                                      */
1770
/*      Low level variant of open that takes the preexisting            */
1771
/*      PCIDSKFile.                                                     */
1772
/************************************************************************/
1773
1774
GDALDataset *PCIDSK2Dataset::LLOpen(const char *pszFilename,
1775
                                    PCIDSK::PCIDSKFile *poFile,
1776
                                    GDALAccess eAccessIn,
1777
                                    char **papszSiblingFiles)
1778
1779
3.29k
{
1780
3.29k
    PCIDSK2Dataset *poDS = new PCIDSK2Dataset();
1781
    /* -------------------------------------------------------------------- */
1782
    /*      Create a corresponding GDALDataset.                             */
1783
    /* -------------------------------------------------------------------- */
1784
3.29k
    poDS->poFile = poFile;
1785
3.29k
    poDS->eAccess = eAccessIn;
1786
3.29k
    poDS->nRasterXSize = poFile->GetWidth();
1787
3.29k
    poDS->nRasterYSize = poFile->GetHeight();
1788
1789
3.29k
    const bool bValidRasterDimensions =
1790
3.29k
        poFile->GetWidth() && poFile->GetHeight();
1791
3.29k
    if (!bValidRasterDimensions)
1792
1.62k
    {
1793
1.62k
        poDS->nRasterXSize = 512;
1794
1.62k
        poDS->nRasterYSize = 512;
1795
1.62k
    }
1796
1797
3.29k
    try
1798
3.29k
    {
1799
1800
        /* --------------------------------------------------------------------
1801
         */
1802
        /*      Are we specifically PIXEL or BAND interleaving? */
1803
        /*                                                                      */
1804
        /*      We don't set anything for FILE since it is harder to know if */
1805
        /*      this is tiled or what the on disk interleaving is. */
1806
        /* --------------------------------------------------------------------
1807
         */
1808
3.29k
        if (EQUAL(poFile->GetInterleaving().c_str(), "PIXEL"))
1809
1
            poDS->SetMetadataItem("IMAGE_STRUCTURE", "PIXEL",
1810
1
                                  "IMAGE_STRUCTURE");
1811
3.29k
        else if (EQUAL(poFile->GetInterleaving().c_str(), "BAND"))
1812
644
            poDS->SetMetadataItem("IMAGE_STRUCTURE", "BAND", "IMAGE_STRUCTURE");
1813
1814
        /* --------------------------------------------------------------------
1815
         */
1816
        /*      Create band objects. */
1817
        /* --------------------------------------------------------------------
1818
         */
1819
3.29k
        for (int iBand = 0;
1820
206k
             bValidRasterDimensions && iBand < poFile->GetChannels(); iBand++)
1821
202k
        {
1822
202k
            PCIDSKChannel *poChannel = poFile->GetChannel(iBand + 1);
1823
202k
            if (poChannel->GetBlockWidth() <= 0 ||
1824
202k
                poChannel->GetBlockHeight() <= 0)
1825
0
            {
1826
0
                delete poDS;
1827
0
                return nullptr;
1828
0
            }
1829
1830
202k
            if (PCIDSK2Dataset::PCIDSKTypeToGDAL(poChannel->GetType()) ==
1831
202k
                GDT_Unknown)
1832
195k
            {
1833
195k
                continue;
1834
195k
            }
1835
1836
7.67k
            poDS->SetBand(poDS->GetRasterCount() + 1,
1837
7.67k
                          new PCIDSK2Band(poFile, poChannel));
1838
7.67k
        }
1839
1840
        /* --------------------------------------------------------------------
1841
         */
1842
        /*      Create band objects for bitmap segments. */
1843
        /* --------------------------------------------------------------------
1844
         */
1845
3.29k
        int nLastBitmapSegment = 0;
1846
3.29k
        PCIDSKSegment *poBitSeg = nullptr;
1847
1848
3.29k
        while (bValidRasterDimensions &&
1849
3.29k
               (poBitSeg = poFile->GetSegment(SEG_BIT, "",
1850
1.60k
                                              nLastBitmapSegment)) != nullptr)
1851
0
        {
1852
0
            PCIDSKChannel *poChannel = dynamic_cast<PCIDSKChannel *>(poBitSeg);
1853
0
            if (poChannel == nullptr || poChannel->GetBlockWidth() <= 0 ||
1854
0
                poChannel->GetBlockHeight() <= 0)
1855
0
            {
1856
0
                delete poDS;
1857
0
                return nullptr;
1858
0
            }
1859
1860
0
            if (PCIDSK2Dataset::PCIDSKTypeToGDAL(poChannel->GetType()) ==
1861
0
                GDT_Unknown)
1862
0
            {
1863
0
                continue;
1864
0
            }
1865
1866
0
            poDS->SetBand(poDS->GetRasterCount() + 1,
1867
0
                          new PCIDSK2Band(poChannel));
1868
1869
0
            nLastBitmapSegment = poBitSeg->GetSegmentNumber();
1870
0
        }
1871
1872
        /* --------------------------------------------------------------------
1873
         */
1874
        /*      Create vector layers from vector segments. */
1875
        /* --------------------------------------------------------------------
1876
         */
1877
3.29k
        PCIDSK::PCIDSKSegment *segobj = poFile->GetSegment(PCIDSK::SEG_VEC, "");
1878
4.66k
        for (; segobj != nullptr;
1879
3.29k
             segobj = poFile->GetSegment(PCIDSK::SEG_VEC, "",
1880
1.36k
                                         segobj->GetSegmentNumber()))
1881
1.36k
        {
1882
1.36k
            PCIDSK::PCIDSKVectorSegment *poVecSeg =
1883
1.36k
                dynamic_cast<PCIDSK::PCIDSKVectorSegment *>(segobj);
1884
1.36k
            if (poVecSeg)
1885
1.36k
                poDS->apoLayers.push_back(new OGRPCIDSKLayer(
1886
1.36k
                    poDS, segobj, poVecSeg, eAccessIn == GA_Update));
1887
1.36k
        }
1888
1889
        /* --------------------------------------------------------------------
1890
         */
1891
        /*      Process RPC segment, if there is one. */
1892
        /* --------------------------------------------------------------------
1893
         */
1894
3.29k
        poDS->ProcessRPC();
1895
1896
        /* --------------------------------------------------------------------
1897
         */
1898
        /*      Initialize any PAM information. */
1899
        /* --------------------------------------------------------------------
1900
         */
1901
3.29k
        poDS->SetDescription(pszFilename);
1902
3.29k
        poDS->TryLoadXML(papszSiblingFiles);
1903
1904
        /* --------------------------------------------------------------------
1905
         */
1906
        /*      Open overviews. */
1907
        /* --------------------------------------------------------------------
1908
         */
1909
3.29k
        poDS->oOvManager.Initialize(poDS, pszFilename, papszSiblingFiles);
1910
1911
3.29k
        return poDS;
1912
3.29k
    }
1913
1914
    /* -------------------------------------------------------------------- */
1915
    /*      Trap exceptions.                                                */
1916
    /* -------------------------------------------------------------------- */
1917
3.29k
    catch (const PCIDSKException &ex)
1918
3.29k
    {
1919
101
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
1920
101
    }
1921
3.29k
    catch (...)
1922
3.29k
    {
1923
0
        CPLError(CE_Failure, CPLE_AppDefined,
1924
0
                 "PCIDSK SDK Failure in Open(), unexpected exception.");
1925
0
    }
1926
1927
    /* -------------------------------------------------------------------- */
1928
    /*      In case of exception, close dataset                             */
1929
    /* -------------------------------------------------------------------- */
1930
101
    delete poDS;
1931
1932
101
    return nullptr;
1933
3.29k
}
1934
1935
/************************************************************************/
1936
/*                               Create()                               */
1937
/************************************************************************/
1938
1939
GDALDataset *PCIDSK2Dataset::Create(const char *pszFilename, int nXSize,
1940
                                    int nYSize, int nBandsIn,
1941
                                    GDALDataType eType, char **papszParamList)
1942
1943
69
{
1944
    /* -------------------------------------------------------------------- */
1945
    /*      Prepare channel type list.                                      */
1946
    /* -------------------------------------------------------------------- */
1947
69
    std::vector<eChanType> aeChanTypes;
1948
1949
69
    if (eType == GDT_Float32)
1950
0
        aeChanTypes.resize(std::max(1, nBandsIn), CHN_32R);
1951
69
    else if (eType == GDT_Int16)
1952
0
        aeChanTypes.resize(std::max(1, nBandsIn), CHN_16S);
1953
69
    else if (eType == GDT_UInt16)
1954
0
        aeChanTypes.resize(std::max(1, nBandsIn), CHN_16U);
1955
69
    else if (eType == GDT_CInt16)
1956
0
        aeChanTypes.resize(std::max(1, nBandsIn), CHN_C16S);
1957
69
    else if (eType == GDT_CFloat32)
1958
0
        aeChanTypes.resize(std::max(1, nBandsIn), CHN_C32R);
1959
69
    else
1960
69
        aeChanTypes.resize(std::max(1, nBandsIn), CHN_8U);
1961
1962
    /* -------------------------------------------------------------------- */
1963
    /*      Reformat options.  Currently no support for jpeg compression    */
1964
    /*      quality.                                                        */
1965
    /* -------------------------------------------------------------------- */
1966
69
    CPLString osOptions;
1967
69
    const char *pszValue = CSLFetchNameValue(papszParamList, "INTERLEAVING");
1968
69
    if (pszValue == nullptr)
1969
69
        pszValue = "BAND";
1970
1971
69
    osOptions = pszValue;
1972
1973
69
    if (osOptions == "TILED")
1974
0
    {
1975
0
        pszValue = CSLFetchNameValue(papszParamList, "TILESIZE");
1976
0
        if (pszValue != nullptr)
1977
0
            osOptions += pszValue;
1978
1979
0
        pszValue = CSLFetchNameValue(papszParamList, "COMPRESSION");
1980
0
        if (pszValue != nullptr)
1981
0
        {
1982
0
            osOptions += " ";
1983
0
            osOptions += pszValue;
1984
0
        }
1985
1986
0
        pszValue = CSLFetchNameValue(papszParamList, "TILEVERSION");
1987
0
        if (pszValue != nullptr)
1988
0
        {
1989
0
            osOptions += " TILEV";
1990
0
            osOptions += pszValue;
1991
0
        }
1992
0
    }
1993
1994
    /* -------------------------------------------------------------------- */
1995
    /*      Try creation.                                                   */
1996
    /* -------------------------------------------------------------------- */
1997
1998
69
    try
1999
69
    {
2000
69
        if (nBandsIn == 0)
2001
69
        {
2002
69
            nXSize = 512;
2003
69
            nYSize = 512;
2004
69
        }
2005
69
        PCIDSKFile *poFile = PCIDSK::Create(pszFilename, nXSize, nYSize,
2006
69
                                            nBandsIn, &(aeChanTypes[0]),
2007
69
                                            osOptions, PCIDSK2GetInterfaces());
2008
2009
        /* --------------------------------------------------------------------
2010
         */
2011
        /*      Apply band descriptions, if provided as creation options. */
2012
        /* --------------------------------------------------------------------
2013
         */
2014
69
        for (size_t i = 0;
2015
69
             papszParamList != nullptr && papszParamList[i] != nullptr; i++)
2016
0
        {
2017
0
            if (STARTS_WITH_CI(papszParamList[i], "BANDDESC"))
2018
0
            {
2019
0
                int nBand = atoi(papszParamList[i] + 8);
2020
0
                const char *pszDescription = strstr(papszParamList[i], "=");
2021
0
                if (pszDescription && nBand > 0 && nBand <= nBandsIn)
2022
0
                {
2023
0
                    poFile->GetChannel(nBand)->SetDescription(pszDescription +
2024
0
                                                              1);
2025
0
                }
2026
0
            }
2027
0
        }
2028
2029
69
        return LLOpen(pszFilename, poFile, GA_Update);
2030
69
    }
2031
    /* -------------------------------------------------------------------- */
2032
    /*      Trap exceptions.                                                */
2033
    /* -------------------------------------------------------------------- */
2034
69
    catch (const PCIDSKException &ex)
2035
69
    {
2036
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
2037
0
    }
2038
69
    catch (...)
2039
69
    {
2040
0
        CPLError(CE_Failure, CPLE_AppDefined,
2041
0
                 "PCIDSK::Create() failed, unexpected exception.");
2042
0
    }
2043
2044
0
    return nullptr;
2045
69
}
2046
2047
/************************************************************************/
2048
/*                           TestCapability()                           */
2049
/************************************************************************/
2050
2051
int PCIDSK2Dataset::TestCapability(const char *pszCap)
2052
2053
2.40k
{
2054
2.40k
    if (EQUAL(pszCap, ODsCCreateLayer))
2055
1.10k
        return eAccess == GA_Update;
2056
1.30k
    if (EQUAL(pszCap, ODsCRandomLayerWrite))
2057
0
        return eAccess == GA_Update;
2058
1.30k
    if (EQUAL(pszCap, ODsCZGeometries))
2059
0
        return TRUE;
2060
2061
1.30k
    return FALSE;
2062
1.30k
}
2063
2064
/************************************************************************/
2065
/*                              GetLayer()                              */
2066
/************************************************************************/
2067
2068
OGRLayer *PCIDSK2Dataset::GetLayer(int iLayer)
2069
2070
30.0k
{
2071
30.0k
    if (iLayer < 0 || iLayer >= static_cast<int>(apoLayers.size()))
2072
0
        return nullptr;
2073
2074
30.0k
    return apoLayers[iLayer];
2075
30.0k
}
2076
2077
/************************************************************************/
2078
/*                           ICreateLayer()                             */
2079
/************************************************************************/
2080
2081
OGRLayer *PCIDSK2Dataset::ICreateLayer(const char *pszLayerName,
2082
                                       const OGRGeomFieldDefn *poGeomFieldDefn,
2083
                                       CSLConstList /*papszOptions*/)
2084
1.10k
{
2085
    /* -------------------------------------------------------------------- */
2086
    /*      Verify we are in update mode.                                   */
2087
    /* -------------------------------------------------------------------- */
2088
1.10k
    if (eAccess != GA_Update)
2089
0
    {
2090
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
2091
0
                 "Data source %s opened read-only.\n"
2092
0
                 "New layer %s cannot be created.\n",
2093
0
                 GetDescription(), pszLayerName);
2094
0
        return nullptr;
2095
0
    }
2096
2097
1.10k
    const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
2098
1.10k
    const auto poSRS =
2099
1.10k
        poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
2100
2101
    /* -------------------------------------------------------------------- */
2102
    /*      Figure out what type of layer we need.                          */
2103
    /* -------------------------------------------------------------------- */
2104
1.10k
    std::string osLayerType;
2105
2106
1.10k
    switch (wkbFlatten(eType))
2107
1.10k
    {
2108
3
        case wkbPoint:
2109
3
            osLayerType = "POINTS";
2110
3
            break;
2111
2112
4
        case wkbLineString:
2113
4
            osLayerType = "ARCS";
2114
4
            break;
2115
2116
1
        case wkbPolygon:
2117
1
            osLayerType = "WHOLE_POLYGONS";
2118
1
            break;
2119
2120
584
        case wkbNone:
2121
584
            osLayerType = "TABLE";
2122
584
            break;
2123
2124
509
        default:
2125
509
            break;
2126
1.10k
    }
2127
2128
    /* -------------------------------------------------------------------- */
2129
    /*      Create the segment.                                             */
2130
    /* -------------------------------------------------------------------- */
2131
1.10k
    int nSegNum;
2132
1.10k
    try
2133
1.10k
    {
2134
1.10k
        nSegNum = poFile->CreateSegment(pszLayerName, "", PCIDSK::SEG_VEC, 0L);
2135
1.10k
    }
2136
1.10k
    catch (const PCIDSKException &ex)
2137
1.10k
    {
2138
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
2139
0
        return nullptr;
2140
0
    }
2141
1.10k
    PCIDSK::PCIDSKSegment *poSeg = poFile->GetSegment(nSegNum);
2142
1.10k
    PCIDSK::PCIDSKVectorSegment *poVecSeg =
2143
1.10k
        dynamic_cast<PCIDSK::PCIDSKVectorSegment *>(poSeg);
2144
1.10k
    if (poVecSeg == nullptr)
2145
0
        return nullptr;
2146
2147
1.10k
    if (osLayerType != "")
2148
592
        poSeg->SetMetadataValue("LAYER_TYPE", osLayerType);
2149
2150
    /* -------------------------------------------------------------------- */
2151
    /*      Do we need to apply a coordinate system?                        */
2152
    /* -------------------------------------------------------------------- */
2153
1.10k
    char *pszGeosys = nullptr;
2154
1.10k
    char *pszUnits = nullptr;
2155
1.10k
    double *padfPrjParams = nullptr;
2156
2157
1.10k
    if (poSRS != nullptr && poSRS->exportToPCI(&pszGeosys, &pszUnits,
2158
77
                                               &padfPrjParams) == OGRERR_NONE)
2159
77
    {
2160
77
        try
2161
77
        {
2162
77
            std::vector<double> adfPCIParameters;
2163
2164
1.38k
            for (int i = 0; i < 17; i++)
2165
1.30k
                adfPCIParameters.push_back(padfPrjParams[i]);
2166
2167
77
            if (STARTS_WITH_CI(pszUnits, "FOOT"))
2168
0
                adfPCIParameters.push_back(static_cast<double>(
2169
0
                    static_cast<int>(PCIDSK::UNIT_US_FOOT)));
2170
77
            else if (STARTS_WITH_CI(pszUnits, "INTL FOOT"))
2171
0
                adfPCIParameters.push_back(static_cast<double>(
2172
0
                    static_cast<int>(PCIDSK::UNIT_INTL_FOOT)));
2173
77
            else if (STARTS_WITH_CI(pszUnits, "DEGREE"))
2174
25
                adfPCIParameters.push_back(
2175
25
                    static_cast<double>(static_cast<int>(PCIDSK::UNIT_DEGREE)));
2176
52
            else
2177
52
                adfPCIParameters.push_back(
2178
52
                    static_cast<double>(static_cast<int>(PCIDSK::UNIT_METER)));
2179
2180
77
            poVecSeg->SetProjection(pszGeosys, adfPCIParameters);
2181
77
        }
2182
77
        catch (const PCIDSKException &ex)
2183
77
        {
2184
0
            CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
2185
0
        }
2186
2187
77
        CPLFree(pszGeosys);
2188
77
        CPLFree(pszUnits);
2189
77
        CPLFree(padfPrjParams);
2190
77
    }
2191
2192
    /* -------------------------------------------------------------------- */
2193
    /*      Create the layer object.                                        */
2194
    /* -------------------------------------------------------------------- */
2195
2196
1.10k
    apoLayers.push_back(new OGRPCIDSKLayer(this, poSeg, poVecSeg, TRUE));
2197
2198
1.10k
    return apoLayers.back();
2199
1.10k
}
2200
2201
/************************************************************************/
2202
/*                        GDALRegister_PCIDSK()                         */
2203
/************************************************************************/
2204
2205
void GDALRegister_PCIDSK()
2206
2207
24
{
2208
24
    if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
2209
0
        return;
2210
2211
24
    GDALDriver *poDriver = new GDALDriver();
2212
24
    PCIDSKDriverSetCommonMetadata(poDriver);
2213
2214
24
    poDriver->pfnOpen = PCIDSK2Dataset::Open;
2215
24
    poDriver->pfnCreate = PCIDSK2Dataset::Create;
2216
2217
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
2218
24
}