Coverage Report

Created: 2025-08-11 09:23

/src/gdal/ogr/ogrsf_frmts/avc/ogravcbinlayer.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OGR
4
 * Purpose:  Implements OGRAVCBinLayer class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogr_avc.h"
14
#include "ogr_api.h"
15
#include "cpl_conv.h"
16
#include "cpl_string.h"
17
18
#include <climits>
19
#include <cstdlib>
20
21
/************************************************************************/
22
/*                           OGRAVCBinLayer()                           */
23
/************************************************************************/
24
25
OGRAVCBinLayer::OGRAVCBinLayer(OGRAVCBinDataSource *poDSIn,
26
                               AVCE00Section *psSectionIn)
27
13.8k
    : OGRAVCLayer(psSectionIn->eType, poDSIn), m_psSection(psSectionIn),
28
13.8k
      hFile(nullptr), poArcLayer(nullptr), bNeedReset(false), hTable(nullptr),
29
13.8k
      nTableBaseField(-1), nTableAttrIndex(-1), nNextFID(1)
30
13.8k
{
31
13.8k
    SetupFeatureDefinition(m_psSection->pszName);
32
33
13.8k
    szTableName[0] = '\0';
34
13.8k
    if (m_psSection->eType == AVCFilePAL)
35
762
        snprintf(szTableName, sizeof(szTableName), "%s.PAT",
36
762
                 poDS->GetCoverageName());
37
13.0k
    else if (m_psSection->eType == AVCFileRPL)
38
5.31k
        snprintf(szTableName, sizeof(szTableName), "%s.PAT%s",
39
5.31k
                 poDS->GetCoverageName(), m_psSection->pszName);
40
7.74k
    else if (m_psSection->eType == AVCFileARC)
41
708
        snprintf(szTableName, sizeof(szTableName), "%s.AAT",
42
708
                 poDS->GetCoverageName());
43
7.03k
    else if (m_psSection->eType == AVCFileLAB)
44
1.61k
    {
45
1.61k
        AVCE00ReadPtr psInfo =
46
1.61k
            static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
47
48
1.61k
        snprintf(szTableName, sizeof(szTableName), "%s.PAT",
49
1.61k
                 poDS->GetCoverageName());
50
51
28.6k
        for (int iSection = 0; iSection < psInfo->numSections; iSection++)
52
26.9k
        {
53
26.9k
            if (psInfo->pasSections[iSection].eType == AVCFilePAL)
54
510
                nTableAttrIndex = poFeatureDefn->GetFieldIndex("PolyId");
55
26.9k
        }
56
1.61k
    }
57
58
13.8k
    CheckSetupTable();
59
13.8k
}
60
61
/************************************************************************/
62
/*                          ~OGRAVCBinLayer()                           */
63
/************************************************************************/
64
65
OGRAVCBinLayer::~OGRAVCBinLayer()
66
67
13.8k
{
68
13.8k
    OGRAVCBinLayer::ResetReading();
69
13.8k
}
70
71
/************************************************************************/
72
/*                            ResetReading()                            */
73
/************************************************************************/
74
75
void OGRAVCBinLayer::ResetReading()
76
77
13.8k
{
78
13.8k
    if (hFile != nullptr)
79
7.87k
    {
80
7.87k
        AVCBinReadClose(hFile);
81
7.87k
        hFile = nullptr;
82
7.87k
    }
83
84
13.8k
    bNeedReset = false;
85
13.8k
    nNextFID = 1;
86
13.8k
    m_bEOF = false;
87
88
13.8k
    if (hTable != nullptr)
89
1.86k
    {
90
1.86k
        AVCBinReadClose(hTable);
91
1.86k
        hTable = nullptr;
92
1.86k
    }
93
13.8k
}
94
95
/************************************************************************/
96
/*                             GetFeature()                             */
97
/************************************************************************/
98
99
OGRFeature *OGRAVCBinLayer::GetFeature(GIntBig nFID)
100
101
211k
{
102
211k
    if (!CPL_INT64_FITS_ON_INT32(nFID))
103
0
        return nullptr;
104
105
    /* -------------------------------------------------------------------- */
106
    /*      If we haven't started yet, open the file now.                   */
107
    /* -------------------------------------------------------------------- */
108
211k
    if (hFile == nullptr)
109
8.77k
    {
110
8.77k
        AVCE00ReadPtr psInfo =
111
8.77k
            static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
112
113
8.77k
        hFile = AVCBinReadOpen(psInfo->pszCoverPath, m_psSection->pszFilename,
114
8.77k
                               psInfo->eCoverType, m_psSection->eType,
115
8.77k
                               psInfo->psDBCSInfo);
116
8.77k
        if (hFile == nullptr)
117
899
            return nullptr;
118
8.77k
    }
119
120
    /* -------------------------------------------------------------------- */
121
    /*      Read the raw feature - the SERIAL_ACCESS_FID fid is a special flag
122
     */
123
    /*      indicating serial access.                                       */
124
    /* -------------------------------------------------------------------- */
125
210k
    void *pFeature = nullptr;
126
127
210k
    if (nFID == SERIAL_ACCESS_FID)
128
192k
    {
129
192k
        while ((pFeature = AVCBinReadNextObject(hFile)) != nullptr &&
130
192k
               !MatchesSpatialFilter(pFeature))
131
0
        {
132
0
            nNextFID++;
133
0
        }
134
192k
    }
135
18.0k
    else
136
18.0k
    {
137
18.0k
        bNeedReset = true;
138
18.0k
        pFeature = AVCBinReadObject(hFile, (int)nFID);
139
18.0k
    }
140
141
210k
    if (pFeature == nullptr)
142
17.0k
        return nullptr;
143
144
    /* -------------------------------------------------------------------- */
145
    /*      Translate the feature.                                          */
146
    /* -------------------------------------------------------------------- */
147
193k
    OGRFeature *poFeature = TranslateFeature(pFeature);
148
193k
    if (poFeature == nullptr)
149
0
        return nullptr;
150
151
    /* -------------------------------------------------------------------- */
152
    /*      LAB's we have to assign the FID to directly, since it           */
153
    /*      doesn't seem to be stored in the file structure.                */
154
    /* -------------------------------------------------------------------- */
155
193k
    if (m_psSection->eType == AVCFileLAB)
156
130k
    {
157
130k
        if (nFID == SERIAL_ACCESS_FID)
158
130k
            poFeature->SetFID(nNextFID++);
159
0
        else
160
0
            poFeature->SetFID(nFID);
161
130k
    }
162
163
    /* -------------------------------------------------------------------- */
164
    /*      If this is a polygon layer, try to assemble the arcs to form    */
165
    /*      the whole polygon geometry.                                     */
166
    /* -------------------------------------------------------------------- */
167
193k
    if (m_psSection->eType == AVCFilePAL || m_psSection->eType == AVCFileRPL)
168
24.5k
        FormPolygonGeometry(poFeature, (AVCPal *)pFeature);
169
170
    /* -------------------------------------------------------------------- */
171
    /*      If we have an attribute table, append the attributes now.       */
172
    /* -------------------------------------------------------------------- */
173
193k
    AppendTableFields(poFeature);
174
175
193k
    return poFeature;
176
193k
}
177
178
/************************************************************************/
179
/*                           GetNextFeature()                           */
180
/************************************************************************/
181
182
OGRFeature *OGRAVCBinLayer::GetNextFeature()
183
184
192k
{
185
192k
    if (m_bEOF)
186
0
        return nullptr;
187
188
192k
    if (bNeedReset)
189
0
        ResetReading();
190
191
192k
    OGRFeature *poFeature = GetFeature(SERIAL_ACCESS_FID);
192
193
    // Skip universe polygon.
194
192k
    if (poFeature != nullptr && poFeature->GetFID() == 1 &&
195
192k
        m_psSection->eType == AVCFilePAL)
196
6
    {
197
6
        OGRFeature::DestroyFeature(poFeature);
198
6
        poFeature = GetFeature(SERIAL_ACCESS_FID);
199
6
    }
200
201
192k
    while (poFeature != nullptr &&
202
192k
           ((m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poFeature)) ||
203
184k
            !FilterGeometry(poFeature->GetGeometryRef())))
204
0
    {
205
0
        OGRFeature::DestroyFeature(poFeature);
206
0
        poFeature = GetFeature(SERIAL_ACCESS_FID);
207
0
    }
208
209
192k
    if (poFeature == nullptr)
210
7.87k
        m_bEOF = true;
211
212
192k
    return poFeature;
213
192k
}
214
215
/************************************************************************/
216
/*                           TestCapability()                           */
217
/************************************************************************/
218
219
int OGRAVCBinLayer::TestCapability(const char *pszCap)
220
221
0
{
222
0
    if (eSectionType == AVCFileARC && EQUAL(pszCap, OLCRandomRead))
223
0
        return TRUE;
224
225
0
    return OGRAVCLayer::TestCapability(pszCap);
226
0
}
227
228
/************************************************************************/
229
/*                        FormPolygonGeometry()                         */
230
/*                                                                      */
231
/*      Collect all the arcs forming edges to this polygon and form     */
232
/*      them into the appropriate OGR geometry on the target feature.   */
233
/************************************************************************/
234
235
bool OGRAVCBinLayer::FormPolygonGeometry(OGRFeature *poFeature, AVCPal *psPAL)
236
237
24.5k
{
238
    /* -------------------------------------------------------------------- */
239
    /*      Try to find the corresponding ARC layer if not already          */
240
    /*      recorded.                                                       */
241
    /* -------------------------------------------------------------------- */
242
24.5k
    if (poArcLayer == nullptr)
243
3.94k
    {
244
32.1k
        for (int i = 0; i < poDS->GetLayerCount(); i++)
245
28.2k
        {
246
28.2k
            OGRAVCBinLayer *poLayer =
247
28.2k
                static_cast<OGRAVCBinLayer *>(poDS->GetLayer(i));
248
249
28.2k
            if (poLayer->eSectionType == AVCFileARC)
250
779
                poArcLayer = poLayer;
251
28.2k
        }
252
253
3.94k
        if (poArcLayer == nullptr)
254
3.17k
            return false;
255
3.94k
    }
256
257
    /* -------------------------------------------------------------------- */
258
    /*      Read all the arcs related to this polygon, making a working     */
259
    /*      copy of them since the one returned by AVC is temporary.        */
260
    /* -------------------------------------------------------------------- */
261
21.3k
    OGRGeometryCollection oArcs;
262
263
44.0k
    for (int iArc = 0; iArc < psPAL->numArcs; iArc++)
264
32.7k
    {
265
32.7k
        if (psPAL->pasArcs[iArc].nArcId == 0 ||
266
32.7k
            psPAL->pasArcs[iArc].nArcId == INT_MIN)
267
8.79k
        {
268
8.79k
            continue;
269
8.79k
        }
270
271
        // If the other side of the line is the same polygon then this
272
        // arc is a "bridge" arc and can be discarded.  If we don't discard
273
        // it, then we should double it as bridge arcs seem to only appear
274
        // once.  But by discarding it we ensure a multi-ring polygon will be
275
        // properly formed.
276
23.9k
        if (psPAL->pasArcs[iArc].nAdjPoly == psPAL->nPolyId)
277
4.97k
            continue;
278
279
18.9k
        OGRFeature *poArc =
280
18.9k
            poArcLayer->GetFeature(std::abs(psPAL->pasArcs[iArc].nArcId));
281
282
18.9k
        if (poArc == nullptr)
283
10.0k
            return false;
284
285
8.94k
        if (poArc->GetGeometryRef() == nullptr)
286
0
            return false;
287
288
8.94k
        oArcs.addGeometry(poArc->GetGeometryRef());
289
8.94k
        OGRFeature::DestroyFeature(poArc);
290
8.94k
    }
291
292
11.3k
    OGRErr eErr;
293
11.3k
    OGRGeometry *poPolygon = OGRGeometry::FromHandle(OGRBuildPolygonFromEdges(
294
11.3k
        (OGRGeometryH)&oArcs, TRUE, FALSE, 0.0, &eErr));
295
11.3k
    if (poPolygon != nullptr)
296
11.3k
    {
297
11.3k
        poPolygon->assignSpatialReference(GetSpatialRef());
298
11.3k
        poFeature->SetGeometryDirectly(poPolygon);
299
11.3k
    }
300
301
11.3k
    return eErr == OGRERR_NONE;
302
21.3k
}
303
304
/************************************************************************/
305
/*                          CheckSetupTable()                           */
306
/*                                                                      */
307
/*      Check if the named table exists, and if so, setup access to     */
308
/*      it (open it), and add its fields to the feature class           */
309
/*      definition.                                                     */
310
/************************************************************************/
311
312
bool OGRAVCBinLayer::CheckSetupTable()
313
314
13.8k
{
315
13.8k
    if (szTableName[0] == '\0')
316
5.42k
        return false;
317
318
    /* -------------------------------------------------------------------- */
319
    /*      Scan for the indicated section.                                 */
320
    /* -------------------------------------------------------------------- */
321
8.40k
    AVCE00ReadPtr psInfo = static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
322
323
8.40k
    AVCE00Section *l_psSection = nullptr;
324
408k
    for (int iSection = 0; iSection < psInfo->numSections; iSection++)
325
399k
    {
326
399k
        if (EQUAL(szTableName,
327
399k
                  CPLString(psInfo->pasSections[iSection].pszName).Trim()) &&
328
399k
            psInfo->pasSections[iSection].eType == AVCFileTABLE)
329
3.36k
            l_psSection = psInfo->pasSections + iSection;
330
399k
    }
331
332
8.40k
    if (l_psSection == nullptr)
333
5.14k
    {
334
5.14k
        szTableName[0] = '\0';
335
5.14k
        return false;
336
5.14k
    }
337
338
    /* -------------------------------------------------------------------- */
339
    /*      Try opening the table.                                          */
340
    /* -------------------------------------------------------------------- */
341
3.26k
    hTable =
342
3.26k
        AVCBinReadOpen(psInfo->pszInfoPath, szTableName, psInfo->eCoverType,
343
3.26k
                       AVCFileTABLE, psInfo->psDBCSInfo);
344
345
3.26k
    if (hTable == nullptr)
346
431
    {
347
431
        szTableName[0] = '\0';
348
431
        return false;
349
431
    }
350
351
    /* -------------------------------------------------------------------- */
352
    /*      Setup attributes.                                               */
353
    /* -------------------------------------------------------------------- */
354
2.83k
    nTableBaseField = poFeatureDefn->GetFieldCount();
355
356
2.83k
    AppendTableDefinition(hTable->hdr.psTableDef);
357
358
    /* -------------------------------------------------------------------- */
359
    /*      Close table so we don't have to many files open at once.        */
360
    /* -------------------------------------------------------------------- */
361
2.83k
    AVCBinReadClose(hTable);
362
363
2.83k
    hTable = nullptr;
364
365
2.83k
    return true;
366
3.26k
}
367
368
/************************************************************************/
369
/*                         AppendTableFields()                          */
370
/************************************************************************/
371
372
bool OGRAVCBinLayer::AppendTableFields(OGRFeature *poFeature)
373
374
193k
{
375
193k
    AVCE00ReadPtr psInfo = static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
376
377
193k
    if (szTableName[0] == '\0')
378
81.6k
        return false;
379
380
    /* -------------------------------------------------------------------- */
381
    /*      Open the table if it is currently closed.                       */
382
    /* -------------------------------------------------------------------- */
383
111k
    if (hTable == nullptr)
384
1.86k
    {
385
1.86k
        hTable =
386
1.86k
            AVCBinReadOpen(psInfo->pszInfoPath, szTableName, psInfo->eCoverType,
387
1.86k
                           AVCFileTABLE, psInfo->psDBCSInfo);
388
1.86k
    }
389
390
111k
    if (hTable == nullptr)
391
0
        return false;
392
393
    /* -------------------------------------------------------------------- */
394
    /*      Read the info record.                                           */
395
    /*                                                                      */
396
    /*      We usually assume the FID of the feature is the key but in a    */
397
    /*      polygon coverage we need to use the PolyId attribute of LAB     */
398
    /*      features to lookup the related attributes.  In this case        */
399
    /*      nTableAttrIndex will already be setup to refer to the           */
400
    /*      PolyId field.                                                   */
401
    /* -------------------------------------------------------------------- */
402
111k
    const int nRecordId = nTableAttrIndex == -1
403
111k
                              ? static_cast<int>(poFeature->GetFID())
404
111k
                              : poFeature->GetFieldAsInteger(nTableAttrIndex);
405
406
111k
    void *hRecord = AVCBinReadObject(hTable, nRecordId);
407
111k
    if (hRecord == nullptr)
408
44.4k
        return false;
409
410
    /* -------------------------------------------------------------------- */
411
    /*      Translate it.                                                   */
412
    /* -------------------------------------------------------------------- */
413
66.9k
    return TranslateTableFields(poFeature, nTableBaseField,
414
66.9k
                                hTable->hdr.psTableDef, (AVCField *)hRecord);
415
111k
}