Coverage Report

Created: 2026-09-26 08:22

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