Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/avc/ogravcbinlayer.cpp
Line
Count
Source
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
14.0k
    : OGRAVCLayer(psSectionIn->eType, poDSIn), m_psSection(psSectionIn),
28
14.0k
      hFile(nullptr), poArcLayer(nullptr), bNeedReset(false), hTable(nullptr),
29
14.0k
      nTableBaseField(-1), nTableAttrIndex(-1), nNextFID(1)
30
14.0k
{
31
14.0k
    SetupFeatureDefinition(m_psSection->pszName);
32
33
14.0k
    szTableName[0] = '\0';
34
14.0k
    if (m_psSection->eType == AVCFilePAL)
35
1.51k
        snprintf(szTableName, sizeof(szTableName), "%s.PAT",
36
1.51k
                 poDS->GetCoverageName());
37
12.5k
    else if (m_psSection->eType == AVCFileRPL)
38
6.21k
        snprintf(szTableName, sizeof(szTableName), "%s.PAT%s",
39
6.21k
                 poDS->GetCoverageName(), m_psSection->pszName);
40
6.34k
    else if (m_psSection->eType == AVCFileARC)
41
1.50k
        snprintf(szTableName, sizeof(szTableName), "%s.AAT",
42
1.50k
                 poDS->GetCoverageName());
43
4.83k
    else if (m_psSection->eType == AVCFileLAB)
44
1.41k
    {
45
1.41k
        AVCE00ReadPtr psInfo =
46
1.41k
            static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
47
48
1.41k
        snprintf(szTableName, sizeof(szTableName), "%s.PAT",
49
1.41k
                 poDS->GetCoverageName());
50
51
22.5k
        for (int iSection = 0; iSection < psInfo->numSections; iSection++)
52
21.1k
        {
53
21.1k
            if (psInfo->pasSections[iSection].eType == AVCFilePAL)
54
416
                nTableAttrIndex = poFeatureDefn->GetFieldIndex("PolyId");
55
21.1k
        }
56
1.41k
    }
57
58
14.0k
    CheckSetupTable();
59
14.0k
}
60
61
/************************************************************************/
62
/*                          ~OGRAVCBinLayer()                           */
63
/************************************************************************/
64
65
OGRAVCBinLayer::~OGRAVCBinLayer()
66
67
14.0k
{
68
14.0k
    OGRAVCBinLayer::ResetReading();
69
14.0k
}
70
71
/************************************************************************/
72
/*                            ResetReading()                            */
73
/************************************************************************/
74
75
void OGRAVCBinLayer::ResetReading()
76
77
14.0k
{
78
14.0k
    if (hFile != nullptr)
79
9.69k
    {
80
9.69k
        AVCBinReadClose(hFile);
81
9.69k
        hFile = nullptr;
82
9.69k
    }
83
84
14.0k
    bNeedReset = false;
85
14.0k
    nNextFID = 1;
86
14.0k
    m_bEOF = false;
87
88
14.0k
    if (hTable != nullptr)
89
1.98k
    {
90
1.98k
        AVCBinReadClose(hTable);
91
1.98k
        hTable = nullptr;
92
1.98k
    }
93
14.0k
}
94
95
/************************************************************************/
96
/*                             GetFeature()                             */
97
/************************************************************************/
98
99
OGRFeature *OGRAVCBinLayer::GetFeature(GIntBig nFID)
100
101
279k
{
102
279k
    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
279k
    if (hFile == nullptr)
109
10.7k
    {
110
10.7k
        AVCE00ReadPtr psInfo =
111
10.7k
            static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
112
113
10.7k
        hFile = AVCBinReadOpen(psInfo->pszCoverPath, m_psSection->pszFilename,
114
10.7k
                               psInfo->eCoverType, m_psSection->eType,
115
10.7k
                               psInfo->psDBCSInfo);
116
10.7k
        if (hFile == nullptr)
117
1.04k
            return nullptr;
118
10.7k
    }
119
120
    /* -------------------------------------------------------------------- */
121
    /*      Read the raw feature - the SERIAL_ACCESS_FID fid is a special flag
122
     */
123
    /*      indicating serial access.                                       */
124
    /* -------------------------------------------------------------------- */
125
278k
    void *pFeature = nullptr;
126
127
278k
    if (nFID == SERIAL_ACCESS_FID)
128
161k
    {
129
161k
        while ((pFeature = AVCBinReadNextObject(hFile)) != nullptr &&
130
152k
               !MatchesSpatialFilter(pFeature))
131
0
        {
132
0
            nNextFID++;
133
0
        }
134
161k
    }
135
116k
    else
136
116k
    {
137
116k
        bNeedReset = true;
138
116k
        pFeature = AVCBinReadObject(hFile, (int)nFID);
139
116k
    }
140
141
278k
    if (pFeature == nullptr)
142
19.5k
        return nullptr;
143
144
    /* -------------------------------------------------------------------- */
145
    /*      Translate the feature.                                          */
146
    /* -------------------------------------------------------------------- */
147
258k
    OGRFeature *poFeature = TranslateFeature(pFeature);
148
258k
    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
258k
    if (m_psSection->eType == AVCFileLAB)
156
120k
    {
157
120k
        if (nFID == SERIAL_ACCESS_FID)
158
120k
            poFeature->SetFID(nNextFID++);
159
0
        else
160
0
            poFeature->SetFID(nFID);
161
120k
    }
162
163
    /* -------------------------------------------------------------------- */
164
    /*      If this is a polygon layer, try to assemble the arcs to form    */
165
    /*      the whole polygon geometry.                                     */
166
    /* -------------------------------------------------------------------- */
167
258k
    if (m_psSection->eType == AVCFilePAL || m_psSection->eType == AVCFileRPL)
168
23.1k
        FormPolygonGeometry(poFeature, (AVCPal *)pFeature);
169
170
    /* -------------------------------------------------------------------- */
171
    /*      If we have an attribute table, append the attributes now.       */
172
    /* -------------------------------------------------------------------- */
173
258k
    AppendTableFields(poFeature);
174
175
258k
    return poFeature;
176
258k
}
177
178
/************************************************************************/
179
/*                           GetNextFeature()                           */
180
/************************************************************************/
181
182
OGRFeature *OGRAVCBinLayer::GetNextFeature()
183
184
161k
{
185
161k
    if (m_bEOF)
186
0
        return nullptr;
187
188
161k
    if (bNeedReset)
189
0
        ResetReading();
190
191
161k
    OGRFeature *poFeature = GetFeature(SERIAL_ACCESS_FID);
192
193
    // Skip universe polygon.
194
161k
    if (poFeature != nullptr && poFeature->GetFID() == 1 &&
195
1.37k
        m_psSection->eType == AVCFilePAL)
196
2
    {
197
2
        OGRFeature::DestroyFeature(poFeature);
198
2
        poFeature = GetFeature(SERIAL_ACCESS_FID);
199
2
    }
200
201
161k
    while (poFeature != nullptr &&
202
152k
           ((m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poFeature)) ||
203
152k
            !FilterGeometry(poFeature->GetGeometryRef())))
204
0
    {
205
0
        OGRFeature::DestroyFeature(poFeature);
206
0
        poFeature = GetFeature(SERIAL_ACCESS_FID);
207
0
    }
208
209
161k
    if (poFeature == nullptr)
210
9.71k
        m_bEOF = true;
211
212
161k
    return poFeature;
213
161k
}
214
215
/************************************************************************/
216
/*                           TestCapability()                           */
217
/************************************************************************/
218
219
int OGRAVCBinLayer::TestCapability(const char *pszCap) const
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
23.1k
{
238
    /* -------------------------------------------------------------------- */
239
    /*      Try to find the corresponding ARC layer if not already          */
240
    /*      recorded.                                                       */
241
    /* -------------------------------------------------------------------- */
242
23.1k
    if (poArcLayer == nullptr)
243
6.01k
    {
244
26.1k
        for (int i = 0; i < poDS->GetLayerCount(); i++)
245
20.1k
        {
246
20.1k
            OGRAVCBinLayer *poLayer =
247
20.1k
                static_cast<OGRAVCBinLayer *>(poDS->GetLayer(i));
248
249
20.1k
            if (poLayer->eSectionType == AVCFileARC)
250
1.80k
                poArcLayer = poLayer;
251
20.1k
        }
252
253
6.01k
        if (poArcLayer == nullptr)
254
4.21k
            return false;
255
6.01k
    }
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
18.9k
    OGRGeometryCollection oArcs;
262
263
158k
    for (int iArc = 0; iArc < psPAL->numArcs; iArc++)
264
150k
    {
265
150k
        if (psPAL->pasArcs[iArc].nArcId == 0 ||
266
124k
            psPAL->pasArcs[iArc].nArcId == INT_MIN)
267
25.9k
        {
268
25.9k
            continue;
269
25.9k
        }
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
124k
        if (psPAL->pasArcs[iArc].nAdjPoly == psPAL->nPolyId)
277
6.82k
            continue;
278
279
117k
        OGRFeature *poArc =
280
117k
            poArcLayer->GetFeature(std::abs(psPAL->pasArcs[iArc].nArcId));
281
282
117k
        if (poArc == nullptr)
283
10.9k
            return false;
284
285
106k
        if (poArc->GetGeometryRef() == nullptr)
286
0
            return false;
287
288
106k
        oArcs.addGeometry(poArc->GetGeometryRef());
289
106k
        OGRFeature::DestroyFeature(poArc);
290
106k
    }
291
292
8.02k
    OGRErr eErr;
293
8.02k
    OGRGeometry *poPolygon = OGRGeometry::FromHandle(OGRBuildPolygonFromEdges(
294
8.02k
        (OGRGeometryH)&oArcs, TRUE, FALSE, 0.0, &eErr));
295
8.02k
    if (poPolygon != nullptr)
296
8.02k
    {
297
8.02k
        poPolygon->assignSpatialReference(GetSpatialRef());
298
8.02k
        poFeature->SetGeometryDirectly(poPolygon);
299
8.02k
    }
300
301
8.02k
    return eErr == OGRERR_NONE;
302
18.9k
}
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
14.0k
{
315
14.0k
    if (szTableName[0] == '\0')
316
3.41k
        return false;
317
318
    /* -------------------------------------------------------------------- */
319
    /*      Scan for the indicated section.                                 */
320
    /* -------------------------------------------------------------------- */
321
10.6k
    AVCE00ReadPtr psInfo = static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
322
323
10.6k
    AVCE00Section *l_psSection = nullptr;
324
357k
    for (int iSection = 0; iSection < psInfo->numSections; iSection++)
325
346k
    {
326
346k
        if (EQUAL(szTableName,
327
346k
                  CPLString(psInfo->pasSections[iSection].pszName).Trim()) &&
328
3.84k
            psInfo->pasSections[iSection].eType == AVCFileTABLE)
329
3.84k
            l_psSection = psInfo->pasSections + iSection;
330
346k
    }
331
332
10.6k
    if (l_psSection == nullptr)
333
6.91k
    {
334
6.91k
        szTableName[0] = '\0';
335
6.91k
        return false;
336
6.91k
    }
337
338
    /* -------------------------------------------------------------------- */
339
    /*      Try opening the table.                                          */
340
    /* -------------------------------------------------------------------- */
341
3.74k
    hTable =
342
3.74k
        AVCBinReadOpen(psInfo->pszInfoPath, szTableName, psInfo->eCoverType,
343
3.74k
                       AVCFileTABLE, psInfo->psDBCSInfo);
344
345
3.74k
    if (hTable == nullptr)
346
384
    {
347
384
        szTableName[0] = '\0';
348
384
        return false;
349
384
    }
350
351
    /* -------------------------------------------------------------------- */
352
    /*      Setup attributes.                                               */
353
    /* -------------------------------------------------------------------- */
354
3.35k
    nTableBaseField = poFeatureDefn->GetFieldCount();
355
356
3.35k
    AppendTableDefinition(hTable->hdr.psTableDef);
357
358
    /* -------------------------------------------------------------------- */
359
    /*      Close table so we don't have to many files open at once.        */
360
    /* -------------------------------------------------------------------- */
361
3.35k
    AVCBinReadClose(hTable);
362
363
3.35k
    hTable = nullptr;
364
365
3.35k
    return true;
366
3.74k
}
367
368
/************************************************************************/
369
/*                         AppendTableFields()                          */
370
/************************************************************************/
371
372
bool OGRAVCBinLayer::AppendTableFields(OGRFeature *poFeature)
373
374
258k
{
375
258k
    AVCE00ReadPtr psInfo = static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
376
377
258k
    if (szTableName[0] == '\0')
378
153k
        return false;
379
380
    /* -------------------------------------------------------------------- */
381
    /*      Open the table if it is currently closed.                       */
382
    /* -------------------------------------------------------------------- */
383
105k
    if (hTable == nullptr)
384
1.98k
    {
385
1.98k
        hTable =
386
1.98k
            AVCBinReadOpen(psInfo->pszInfoPath, szTableName, psInfo->eCoverType,
387
1.98k
                           AVCFileTABLE, psInfo->psDBCSInfo);
388
1.98k
    }
389
390
105k
    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
105k
    const int nRecordId = nTableAttrIndex == -1
403
105k
                              ? static_cast<int>(poFeature->GetFID())
404
105k
                              : poFeature->GetFieldAsInteger(nTableAttrIndex);
405
406
105k
    void *hRecord = AVCBinReadObject(hTable, nRecordId);
407
105k
    if (hRecord == nullptr)
408
36.1k
        return false;
409
410
    /* -------------------------------------------------------------------- */
411
    /*      Translate it.                                                   */
412
    /* -------------------------------------------------------------------- */
413
69.4k
    return TranslateTableFields(poFeature, nTableBaseField,
414
69.4k
                                hTable->hdr.psTableDef, (AVCField *)hRecord);
415
105k
}