• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 12706066811

10 Jan 2025 08:38AM UTC coverage: 70.084% (-2.5%) from 72.549%
12706066811

Pull #11629

github

web-flow
Merge 9418dc48f into 0df468c56
Pull Request #11629: add uv documentation for python package

563296 of 803749 relevant lines covered (70.08%)

223434.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.65
/frmts/ctg/ctgdataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  CTG driver
4
 * Purpose:  GDALDataset driver for CTG dataset.
5
 * Author:   Even Rouault, <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12

13
#include "gdal_frmts.h"
14
#include "gdal_pam.h"
15
#include "ogr_spatialref.h"
16

17
constexpr int HEADER_LINE_COUNT = 5;
18

19
typedef struct
20
{
21
    int nCode;
22
    const char *pszDesc;
23
} LULCDescStruct;
24

25
static const LULCDescStruct asLULCDesc[] = {
26
    {1, "Urban or Built-Up Land"},
27
    {2, "Agricultural Land"},
28
    {3, "Rangeland"},
29
    {4, "Forest Land"},
30
    {5, "Water"},
31
    {6, "Wetland"},
32
    {7, "Barren Land"},
33
    {8, "Tundra"},
34
    {9, "Perennial Snow and Ice"},
35
    {11, "Residential"},
36
    {12, "Commercial Services"},
37
    {13, "Industrial"},
38
    {14, "Transportation, Communications"},
39
    {15, "Industrial and Commercial"},
40
    {16, "Mixed Urban or Built-Up Land"},
41
    {17, "Other Urban or Built-Up Land"},
42
    {21, "Cropland and Pasture"},
43
    {22, "Orchards, Groves, Vineyards, Nurseries"},
44
    {23, "Confined Feeding Operations"},
45
    {24, "Other Agricultural Land"},
46
    {31, "Herbaceous Rangeland"},
47
    {32, "Shrub and Brush Rangeland"},
48
    {33, "Mixed Rangeland"},
49
    {41, "Deciduous Forest Land"},
50
    {42, "Evergreen Forest Land"},
51
    {43, "Mixed Forest Land"},
52
    {51, "Streams and Canals"},
53
    {52, "Lakes"},
54
    {53, "Reservoirs"},
55
    {54, "Bays and Estuaries"},
56
    {61, "Forested Wetlands"},
57
    {62, "Nonforested Wetlands"},
58
    {71, "Dry Salt Flats"},
59
    {72, "Beaches"},
60
    {73, "Sandy Areas Other than Beaches"},
61
    {74, "Bare Exposed Rock"},
62
    {75, "Strip Mines, Quarries, and Gravel Pits"},
63
    {76, "Transitional Areas"},
64
    {77, "Mixed Barren Land"},
65
    {81, "Shrub and Brush Tundra"},
66
    {82, "Herbaceous Tundra"},
67
    {83, "Bare Ground"},
68
    {84, "Wet Tundra"},
69
    {85, "Mixed Tundra"},
70
    {91, "Perennial Snowfields"},
71
    {92, "Glaciers"}};
72

73
static const char *const apszBandDescription[] = {
74
    "Land Use and Land Cover",
75
    "Political units",
76
    "Census county subdivisions and SMSA tracts",
77
    "Hydrologic units",
78
    "Federal land ownership",
79
    "State land ownership"};
80

81
/************************************************************************/
82
/* ==================================================================== */
83
/*                              CTGDataset                              */
84
/* ==================================================================== */
85
/************************************************************************/
86

87
class CTGRasterBand;
88

89
class CTGDataset final : public GDALPamDataset
90
{
91
    friend class CTGRasterBand;
92

93
    VSILFILE *fp;
94

95
    int nNWEasting, nNWNorthing, nCellSize, nUTMZone;
96
    OGRSpatialReference m_oSRS{};
97

98
    int bHasReadImagery;
99
    GByte *pabyImage;
100

101
    int ReadImagery();
102

103
    static const char *ExtractField(char *szOutput, const char *pszBuffer,
104
                                    int nOffset, int nLength);
105

106
  public:
107
    CTGDataset();
108
    ~CTGDataset() override;
109

110
    CPLErr GetGeoTransform(double *) override;
111

112
    const OGRSpatialReference *GetSpatialRef() const override
1✔
113
    {
114
        return &m_oSRS;
1✔
115
    }
116

117
    static GDALDataset *Open(GDALOpenInfo *);
118
    static int Identify(GDALOpenInfo *);
119
};
120

121
/************************************************************************/
122
/* ==================================================================== */
123
/*                            CTGRasterBand                             */
124
/* ==================================================================== */
125
/************************************************************************/
126

127
class CTGRasterBand final : public GDALPamRasterBand
128
{
129
    friend class CTGDataset;
130

131
    char **papszCategories;
132

133
  public:
134
    CTGRasterBand(CTGDataset *, int);
135
    ~CTGRasterBand() override;
136

137
    CPLErr IReadBlock(int, int, void *) override;
138
    double GetNoDataValue(int *pbSuccess = nullptr) override;
139
    char **GetCategoryNames() override;
140
};
141

142
/************************************************************************/
143
/*                           CTGRasterBand()                            */
144
/************************************************************************/
145

146
CTGRasterBand::CTGRasterBand(CTGDataset *poDSIn, int nBandIn)
18✔
147
    : papszCategories(nullptr)
18✔
148
{
149
    poDS = poDSIn;
18✔
150
    nBand = nBandIn;
18✔
151

152
    eDataType = GDT_Int32;
18✔
153

154
    nBlockXSize = poDS->GetRasterXSize();
18✔
155
    nBlockYSize = poDS->GetRasterYSize();
18✔
156
}
18✔
157

158
/************************************************************************/
159
/*                          ~CTGRasterBand()                            */
160
/************************************************************************/
161

162
CTGRasterBand::~CTGRasterBand()
36✔
163

164
{
165
    CSLDestroy(papszCategories);
18✔
166
}
36✔
167

168
/************************************************************************/
169
/*                             IReadBlock()                             */
170
/************************************************************************/
171

172
CPLErr CTGRasterBand::IReadBlock(int /* nBlockXOff */, int /* nBlockYOff */,
1✔
173
                                 void *pImage)
174
{
175
    CTGDataset *poGDS = (CTGDataset *)poDS;
1✔
176

177
    poGDS->ReadImagery();
1✔
178
    memcpy(pImage,
1✔
179
           poGDS->pabyImage +
1✔
180
               sizeof(int) * (nBand - 1) * nBlockXSize * nBlockYSize,
1✔
181
           sizeof(int) * nBlockXSize * nBlockYSize);
1✔
182

183
    return CE_None;
1✔
184
}
185

186
/************************************************************************/
187
/*                           GetNoDataValue()                           */
188
/************************************************************************/
189

190
double CTGRasterBand::GetNoDataValue(int *pbSuccess)
1✔
191
{
192
    if (pbSuccess)
1✔
193
        *pbSuccess = TRUE;
1✔
194

195
    return 0.0;
1✔
196
}
197

198
/************************************************************************/
199
/*                          GetCategoryNames()                          */
200
/************************************************************************/
201

202
char **CTGRasterBand::GetCategoryNames()
2✔
203
{
204
    if (nBand != 1)
2✔
205
        return nullptr;
1✔
206

207
    if (papszCategories != nullptr)
1✔
208
        return papszCategories;
×
209

210
    int nasLULCDescSize = (int)(sizeof(asLULCDesc) / sizeof(asLULCDesc[0]));
1✔
211
    int nCategoriesSize = asLULCDesc[nasLULCDescSize - 1].nCode;
1✔
212
    papszCategories = (char **)CPLCalloc(nCategoriesSize + 2, sizeof(char *));
1✔
213
    for (int i = 0; i < nasLULCDescSize; i++)
47✔
214
    {
215
        papszCategories[asLULCDesc[i].nCode] = CPLStrdup(asLULCDesc[i].pszDesc);
46✔
216
    }
217
    for (int i = 0; i < nCategoriesSize; i++)
93✔
218
    {
219
        if (papszCategories[i] == nullptr)
92✔
220
            papszCategories[i] = CPLStrdup("");
47✔
221
    }
222
    papszCategories[nCategoriesSize + 1] = nullptr;
1✔
223

224
    return papszCategories;
1✔
225
}
226

227
/************************************************************************/
228
/*                            ~CTGDataset()                            */
229
/************************************************************************/
230

231
CTGDataset::CTGDataset()
3✔
232
    : fp(nullptr), nNWEasting(0), nNWNorthing(0), nCellSize(0), nUTMZone(0),
233
      bHasReadImagery(FALSE), pabyImage(nullptr)
3✔
234
{
235
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3✔
236
}
3✔
237

238
/************************************************************************/
239
/*                            ~CTGDataset()                            */
240
/************************************************************************/
241

242
CTGDataset::~CTGDataset()
6✔
243

244
{
245
    CPLFree(pabyImage);
3✔
246
    if (fp != nullptr)
3✔
247
        VSIFCloseL(fp);
3✔
248
}
6✔
249

250
/************************************************************************/
251
/*                              ExtractField()                          */
252
/************************************************************************/
253

254
const char *CTGDataset::ExtractField(char *szField, const char *pszBuffer,
69✔
255
                                     int nOffset, int nLength)
256
{
257
    CPLAssert(nLength <= 10);
69✔
258
    memcpy(szField, pszBuffer + nOffset, nLength);
69✔
259
    szField[nLength] = 0;
69✔
260
    return szField;
69✔
261
}
262

263
/************************************************************************/
264
/*                            ReadImagery()                             */
265
/************************************************************************/
266

267
int CTGDataset::ReadImagery()
1✔
268
{
269
    if (bHasReadImagery)
1✔
270
        return TRUE;
×
271

272
    bHasReadImagery = TRUE;
1✔
273

274
    char szLine[81];
275
    char szField[11];
276
    szLine[80] = 0;
1✔
277
    int nLine = HEADER_LINE_COUNT;
1✔
278
    VSIFSeekL(fp, nLine * 80, SEEK_SET);
1✔
279
    const int nCells = nRasterXSize * nRasterYSize;
1✔
280
    while (VSIFReadL(szLine, 1, 80, fp) == 80)
2✔
281
    {
282
        const int nZone = atoi(ExtractField(szField, szLine, 0, 3));
1✔
283
        if (nZone != nUTMZone)
1✔
284
        {
285
            CPLError(CE_Failure, CPLE_AppDefined,
×
286
                     "Read error at line %d, %s. Did not expected UTM zone %d",
287
                     nLine, szLine, nZone);
288
            return FALSE;
×
289
        }
290
        const int nX =
291
            atoi(ExtractField(szField, szLine, 3, 8)) - nCellSize / 2;
1✔
292
        const int nY =
293
            atoi(ExtractField(szField, szLine, 11, 8)) + nCellSize / 2;
1✔
294
        const GIntBig nDiffX = static_cast<GIntBig>(nX) - nNWEasting;
1✔
295
        const GIntBig nDiffY = static_cast<GIntBig>(nNWNorthing) - nY;
1✔
296
        if (nDiffX < 0 || (nDiffX % nCellSize) != 0 || nDiffY < 0 ||
1✔
297
            (nDiffY % nCellSize) != 0)
1✔
298
        {
299
            CPLError(CE_Failure, CPLE_AppDefined,
×
300
                     "Read error at line %d, %s. Unexpected cell coordinates",
301
                     nLine, szLine);
302
            return FALSE;
×
303
        }
304
        const GIntBig nCellX = nDiffX / nCellSize;
1✔
305
        const GIntBig nCellY = nDiffY / nCellSize;
1✔
306
        if (nCellX >= nRasterXSize || nCellY >= nRasterYSize)
1✔
307
        {
308
            CPLError(CE_Failure, CPLE_AppDefined,
×
309
                     "Read error at line %d, %s. Unexpected cell coordinates",
310
                     nLine, szLine);
311
            return FALSE;
×
312
        }
313
        for (int i = 0; i < 6; i++)
7✔
314
        {
315
            int nVal = atoi(ExtractField(szField, szLine, 20 + 10 * i, 10));
6✔
316
            if (nVal >= 2000000000)
6✔
317
                nVal = 0;
×
318
            ((int *)
319
                 pabyImage)[i * nCells +
6✔
320
                            static_cast<int>(nCellY) * nRasterXSize + nCellX] =
6✔
321
                nVal;
322
        }
323

324
        nLine++;
1✔
325
    }
326

327
    return TRUE;
1✔
328
}
329

330
/************************************************************************/
331
/*                             Identify()                               */
332
/************************************************************************/
333

334
int CTGDataset::Identify(GDALOpenInfo *poOpenInfo)
51,056✔
335
{
336
    CPLString osFilename;  // let in that scope
102,088✔
337

338
    GDALOpenInfo *poOpenInfoToDelete = nullptr;
51,048✔
339
    /*  GZipped grid_cell.gz files are common, so automagically open them */
340
    /*  if the /vsigzip/ has not been explicitly passed */
341
    const char *pszFilename = CPLGetFilename(poOpenInfo->pszFilename);
51,048✔
342
    if ((EQUAL(pszFilename, "grid_cell.gz") ||
51,045✔
343
         EQUAL(pszFilename, "grid_cell1.gz") ||
51,054✔
344
         EQUAL(pszFilename, "grid_cell2.gz")) &&
51,054✔
345
        !STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
×
346
    {
347
        osFilename = "/vsigzip/";
×
348
        osFilename += poOpenInfo->pszFilename;
×
349
        poOpenInfo = poOpenInfoToDelete = new GDALOpenInfo(
×
350
            osFilename.c_str(), GA_ReadOnly, poOpenInfo->GetSiblingFiles());
×
351
    }
352

353
    if (poOpenInfo->nHeaderBytes < HEADER_LINE_COUNT * 80)
51,045✔
354
    {
355
        delete poOpenInfoToDelete;
48,762✔
356
        return FALSE;
48,749✔
357
    }
358

359
    /* -------------------------------------------------------------------- */
360
    /*      Check that it looks roughly as a CTG dataset                    */
361
    /* -------------------------------------------------------------------- */
362
    const char *pszData = (const char *)poOpenInfo->pabyHeader;
2,283✔
363
    for (int i = 0; i < 4 * 80; i++)
4,908✔
364
    {
365
        if (!((pszData[i] >= '0' && pszData[i] <= '9') || pszData[i] == ' ' ||
4,901✔
366
              pszData[i] == '-'))
2,278✔
367
        {
368
            delete poOpenInfoToDelete;
2,276✔
369
            return FALSE;
2,276✔
370
        }
371
    }
372

373
    char szField[11];
374
    int nRows = atoi(ExtractField(szField, pszData, 0, 10));
7✔
375
    int nCols = atoi(ExtractField(szField, pszData, 20, 10));
7✔
376
    int nMinColIndex = atoi(ExtractField(szField, pszData + 80, 0, 5));
7✔
377
    int nMinRowIndex = atoi(ExtractField(szField, pszData + 80, 5, 5));
7✔
378
    int nMaxColIndex = atoi(ExtractField(szField, pszData + 80, 10, 5));
7✔
379
    int nMaxRowIndex = atoi(ExtractField(szField, pszData + 80, 15, 5));
7✔
380

381
    if (nRows <= 0 || nCols <= 0 || nMinColIndex != 1 || nMinRowIndex != 1 ||
7✔
382
        nMaxRowIndex != nRows || nMaxColIndex != nCols)
6✔
383
    {
384
        delete poOpenInfoToDelete;
1✔
385
        return FALSE;
1✔
386
    }
387

388
    delete poOpenInfoToDelete;
6✔
389
    return TRUE;
6✔
390
}
391

392
/************************************************************************/
393
/*                                Open()                                */
394
/************************************************************************/
395

396
GDALDataset *CTGDataset::Open(GDALOpenInfo *poOpenInfo)
3✔
397

398
{
399
    if (!Identify(poOpenInfo))
3✔
400
        return nullptr;
×
401

402
    CPLString osFilename(poOpenInfo->pszFilename);
6✔
403

404
    /*  GZipped grid_cell.gz files are common, so automagically open them */
405
    /*  if the /vsigzip/ has not been explicitly passed */
406
    const char *pszFilename = CPLGetFilename(poOpenInfo->pszFilename);
3✔
407
    if ((EQUAL(pszFilename, "grid_cell.gz") ||
3✔
408
         EQUAL(pszFilename, "grid_cell1.gz") ||
3✔
409
         EQUAL(pszFilename, "grid_cell2.gz")) &&
3✔
410
        !STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
×
411
    {
412
        osFilename = "/vsigzip/";
×
413
        osFilename += poOpenInfo->pszFilename;
×
414
    }
415

416
    if (poOpenInfo->eAccess == GA_Update)
3✔
417
    {
418
        CPLError(CE_Failure, CPLE_NotSupported,
×
419
                 "The CTG driver does not support update access to existing"
420
                 " datasets.\n");
421
        return nullptr;
×
422
    }
423

424
    /* -------------------------------------------------------------------- */
425
    /*      Find dataset characteristics                                    */
426
    /* -------------------------------------------------------------------- */
427
    VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "rb");
3✔
428
    if (fp == nullptr)
3✔
429
        return nullptr;
×
430

431
    char szHeader[HEADER_LINE_COUNT * 80 + 1];
432
    szHeader[HEADER_LINE_COUNT * 80] = 0;
3✔
433
    if (VSIFReadL(szHeader, 1, HEADER_LINE_COUNT * 80, fp) !=
3✔
434
        HEADER_LINE_COUNT * 80)
435
    {
436
        VSIFCloseL(fp);
×
437
        return nullptr;
×
438
    }
439

440
    for (int i = HEADER_LINE_COUNT * 80 - 1; i >= 0; i--)
216✔
441
    {
442
        if (szHeader[i] == ' ')
216✔
443
            szHeader[i] = 0;
213✔
444
        else
445
            break;
3✔
446
    }
447

448
    char szField[11];
449
    int nRows = atoi(ExtractField(szField, szHeader, 0, 10));
3✔
450
    int nCols = atoi(ExtractField(szField, szHeader, 20, 10));
3✔
451

452
    /* -------------------------------------------------------------------- */
453
    /*      Create a corresponding GDALDataset.                             */
454
    /* -------------------------------------------------------------------- */
455
    CTGDataset *poDS = new CTGDataset();
3✔
456
    poDS->fp = fp;
3✔
457
    fp = nullptr;
3✔
458
    poDS->nRasterXSize = nCols;
3✔
459
    poDS->nRasterYSize = nRows;
3✔
460

461
    poDS->SetMetadataItem("TITLE", szHeader + 4 * 80);
3✔
462

463
    poDS->nCellSize = atoi(ExtractField(szField, szHeader, 35, 5));
3✔
464
    if (poDS->nCellSize <= 0 || poDS->nCellSize >= 10000)
3✔
465
    {
466
        delete poDS;
×
467
        return nullptr;
×
468
    }
469
    poDS->nNWEasting = atoi(ExtractField(szField, szHeader + 3 * 80, 40, 10));
3✔
470
    poDS->nNWNorthing = atoi(ExtractField(szField, szHeader + 3 * 80, 50, 10));
3✔
471
    poDS->nUTMZone = atoi(ExtractField(szField, szHeader, 50, 5));
3✔
472
    if (poDS->nUTMZone <= 0 || poDS->nUTMZone > 60)
3✔
473
    {
474
        delete poDS;
×
475
        return nullptr;
×
476
    }
477

478
    poDS->m_oSRS.importFromEPSG(32600 + poDS->nUTMZone);
3✔
479

480
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
3✔
481
    {
482
        delete poDS;
×
483
        return nullptr;
×
484
    }
485

486
    /* -------------------------------------------------------------------- */
487
    /*      Read the imagery                                                */
488
    /* -------------------------------------------------------------------- */
489
    GByte *pabyImage =
490
        (GByte *)VSICalloc(static_cast<size_t>(nCols) * nRows, 6 * sizeof(int));
3✔
491
    if (pabyImage == nullptr)
3✔
492
    {
493
        delete poDS;
×
494
        return nullptr;
×
495
    }
496
    poDS->pabyImage = pabyImage;
3✔
497

498
    /* -------------------------------------------------------------------- */
499
    /*      Create band information objects.                                */
500
    /* -------------------------------------------------------------------- */
501
    poDS->nBands = 6;
3✔
502
    for (int i = 0; i < poDS->nBands; i++)
21✔
503
    {
504
        poDS->SetBand(i + 1, new CTGRasterBand(poDS, i + 1));
18✔
505
        poDS->GetRasterBand(i + 1)->SetDescription(apszBandDescription[i]);
18✔
506
    }
507

508
    /* -------------------------------------------------------------------- */
509
    /*      Initialize any PAM information.                                 */
510
    /* -------------------------------------------------------------------- */
511
    poDS->SetDescription(poOpenInfo->pszFilename);
3✔
512
    poDS->TryLoadXML();
3✔
513

514
    /* -------------------------------------------------------------------- */
515
    /*      Support overviews.                                              */
516
    /* -------------------------------------------------------------------- */
517
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
3✔
518

519
    return poDS;
3✔
520
}
521

522
/************************************************************************/
523
/*                          GetGeoTransform()                           */
524
/************************************************************************/
525

526
CPLErr CTGDataset::GetGeoTransform(double *padfTransform)
1✔
527

528
{
529
    padfTransform[0] = static_cast<double>(nNWEasting) - nCellSize / 2;
1✔
530
    padfTransform[1] = nCellSize;
1✔
531
    padfTransform[2] = 0;
1✔
532
    padfTransform[3] = static_cast<double>(nNWNorthing) + nCellSize / 2;
1✔
533
    padfTransform[4] = 0.;
1✔
534
    padfTransform[5] = -nCellSize;
1✔
535

536
    return CE_None;
1✔
537
}
538

539
/************************************************************************/
540
/*                         GDALRegister_CTG()                           */
541
/************************************************************************/
542

543
void GDALRegister_CTG()
1,686✔
544

545
{
546
    if (GDALGetDriverByName("CTG") != nullptr)
1,686✔
547
        return;
303✔
548

549
    GDALDriver *poDriver = new GDALDriver();
1,383✔
550

551
    poDriver->SetDescription("CTG");
1,383✔
552
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,383✔
553
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1,383✔
554
                              "USGS LULC Composite Theme Grid");
1,383✔
555
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/ctg.html");
1,383✔
556

557
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,383✔
558

559
    poDriver->pfnOpen = CTGDataset::Open;
1,383✔
560
    poDriver->pfnIdentify = CTGDataset::Identify;
1,383✔
561

562
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,383✔
563
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc