Coverage Report

Created: 2025-08-11 09:23

/src/gdal/ogr/ogrsf_frmts/s57/ogrs57driver.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  S-57 Translator
4
 * Purpose:  Implements OGRS57Driver
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 * Copyright (c) 2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "ogr_s57.h"
15
#include "cpl_conv.h"
16
#include "cpl_multiproc.h"
17
18
S57ClassRegistrar *OGRS57Driver::poRegistrar = nullptr;
19
static CPLMutex *hS57RegistrarMutex = nullptr;
20
21
/************************************************************************/
22
/*                            OGRS57Driver()                            */
23
/************************************************************************/
24
25
OGRS57Driver::OGRS57Driver()
26
24
{
27
24
}
28
29
/************************************************************************/
30
/*                           ~OGRS57Driver()                            */
31
/************************************************************************/
32
33
OGRS57Driver::~OGRS57Driver()
34
35
0
{
36
0
    if (poRegistrar != nullptr)
37
0
    {
38
0
        delete poRegistrar;
39
0
        poRegistrar = nullptr;
40
0
    }
41
42
0
    if (hS57RegistrarMutex != nullptr)
43
0
    {
44
0
        CPLDestroyMutex(hS57RegistrarMutex);
45
0
        hS57RegistrarMutex = nullptr;
46
0
    }
47
0
}
48
49
/************************************************************************/
50
/*                          OGRS57DriverIdentify()                      */
51
/************************************************************************/
52
53
static int OGRS57DriverIdentify(GDALOpenInfo *poOpenInfo)
54
55
164k
{
56
164k
    if (poOpenInfo->nHeaderBytes < 10)
57
61.0k
        return false;
58
103k
    const char *pachLeader = reinterpret_cast<char *>(poOpenInfo->pabyHeader);
59
103k
    if ((pachLeader[5] != '1' && pachLeader[5] != '2' &&
60
103k
         pachLeader[5] != '3') ||
61
103k
        pachLeader[6] != 'L' || (pachLeader[8] != '1' && pachLeader[8] != ' '))
62
102k
    {
63
102k
        return false;
64
102k
    }
65
    // Test for S-57 DSID field structure (to distinguish it from S-101)
66
815
    return strstr(pachLeader, "DSID") != nullptr &&
67
815
           (strstr(pachLeader,
68
761
                   "RCNM!RCID!EXPP!INTU!DSNM!EDTN!UPDN!UADT!ISDT!"
69
761
                   "STED!PRSP!PSDN!PRED!PROF!AGEN!COMT") != nullptr ||
70
            // Below is for autotest/ogr/data/s57/fake_s57.000 fake dataset that has a shortened structure
71
761
            strstr(pachLeader, "RCNM!RCID!EXPP!xxxx") != nullptr);
72
103k
}
73
74
/************************************************************************/
75
/*                                Open()                                */
76
/************************************************************************/
77
78
GDALDataset *OGRS57Driver::Open(GDALOpenInfo *poOpenInfo)
79
80
373
{
81
373
    if (!OGRS57DriverIdentify(poOpenInfo))
82
0
        return nullptr;
83
84
373
    OGRS57DataSource *poDS = new OGRS57DataSource(poOpenInfo->papszOpenOptions);
85
373
    if (!poDS->Open(poOpenInfo->pszFilename))
86
57
    {
87
57
        delete poDS;
88
57
        poDS = nullptr;
89
57
    }
90
91
373
    if (poDS && poOpenInfo->eAccess == GA_Update)
92
0
    {
93
0
        delete poDS;
94
0
        CPLError(CE_Failure, CPLE_OpenFailed,
95
0
                 "S57 Driver doesn't support update.");
96
0
        return nullptr;
97
0
    }
98
99
373
    return poDS;
100
373
}
101
102
/************************************************************************/
103
/*                              Create()                                */
104
/************************************************************************/
105
106
GDALDataset *OGRS57Driver::Create(const char *pszName, int /* nBands */,
107
                                  int /* nXSize */, int /* nYSize */,
108
                                  GDALDataType /* eDT */, char **papszOptions)
109
0
{
110
0
    OGRS57DataSource *poDS = new OGRS57DataSource();
111
112
0
    if (poDS->Create(pszName, papszOptions))
113
0
        return poDS;
114
115
0
    delete poDS;
116
0
    return nullptr;
117
0
}
118
119
/************************************************************************/
120
/*                          GetS57Registrar()                           */
121
/************************************************************************/
122
123
S57ClassRegistrar *OGRS57Driver::GetS57Registrar()
124
125
316
{
126
    /* -------------------------------------------------------------------- */
127
    /*      Instantiate the class registrar if possible.                    */
128
    /* -------------------------------------------------------------------- */
129
316
    CPLMutexHolderD(&hS57RegistrarMutex);
130
131
316
    if (poRegistrar == nullptr)
132
316
    {
133
316
        poRegistrar = new S57ClassRegistrar();
134
135
316
        if (!poRegistrar->LoadInfo(nullptr, nullptr, false))
136
316
        {
137
316
            delete poRegistrar;
138
316
            poRegistrar = nullptr;
139
316
        }
140
316
    }
141
142
316
    return poRegistrar;
143
316
}
144
145
/************************************************************************/
146
/*                           RegisterOGRS57()                           */
147
/************************************************************************/
148
149
void RegisterOGRS57()
150
151
24
{
152
24
    if (GDALGetDriverByName("S57") != nullptr)
153
0
        return;
154
155
24
    GDALDriver *poDriver = new OGRS57Driver();
156
157
24
    poDriver->SetDescription("S57");
158
24
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
159
24
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "IHO S-57 (ENC)");
160
24
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "000");
161
24
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/s57.html");
162
24
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
163
164
24
    poDriver->SetMetadataItem(
165
24
        GDAL_DMD_OPENOPTIONLIST,
166
24
        "<OpenOptionList>"
167
24
        "  <Option name='" S57O_UPDATES
168
24
        "' type='string-select' description='Should update files be "
169
24
        "incorporated into the base data on the fly' default='APPLY'>"
170
24
        "    <Value>APPLY</Value>"
171
24
        "    <Value>IGNORE</Value>"
172
24
        "  </Option>"
173
24
        "  <Option name='" S57O_SPLIT_MULTIPOINT
174
24
        "' type='boolean' description='Should multipoint soundings be split "
175
24
        "into many single point sounding features' default='NO'/>"
176
24
        "  <Option name='" S57O_ADD_SOUNDG_DEPTH
177
24
        "' type='boolean' description='Should a DEPTH attribute be added on "
178
24
        "SOUNDG features and assign the depth of the sounding' default='NO'/>"
179
24
        "  <Option name='" S57O_RETURN_PRIMITIVES
180
24
        "' type='boolean' description='Should all the low level geometry "
181
24
        "primitives be returned as special IsolatedNode, ConnectedNode, Edge "
182
24
        "and Face layers' default='NO'/>"
183
24
        "  <Option name='" S57O_PRESERVE_EMPTY_NUMBERS
184
24
        "' type='boolean' description='If enabled, numeric attributes assigned "
185
24
        "an empty string as a value will be preserved as a special numeric "
186
24
        "value' default='NO'/>"
187
24
        "  <Option name='" S57O_LNAM_REFS
188
24
        "' type='boolean' description='Should LNAM and LNAM_REFS fields be "
189
24
        "attached to features capturing the feature to feature relationships "
190
24
        "in the FFPT group of the S-57 file' default='NO'/>"
191
24
        "  <Option name='" S57O_RETURN_LINKAGES
192
24
        "' type='boolean' description='Should additional attributes relating "
193
24
        "features to their underlying geometric primitives be attached' "
194
24
        "default='NO'/>"
195
24
        "  <Option name='" S57O_RECODE_BY_DSSI
196
24
        "' type='boolean' description='Should attribute values be recoded to "
197
24
        "UTF-8 from the character encoding specified in the S57 DSSI record.' "
198
24
        "default='YES'/>"
199
24
        "  <Option name='" S57O_LIST_AS_STRING
200
24
        "' type='boolean' description='Whether attributes tagged as list in "
201
24
        "S57 dictionaries should be reported as a String field' default='NO'/>"
202
24
        "</OpenOptionList>");
203
24
    poDriver->SetMetadataItem(
204
24
        GDAL_DMD_CREATIONOPTIONLIST,
205
24
        "<CreationOptionList>"
206
24
        "   <Option name='S57_EXPP' type='int' description='Exchange purpose' "
207
24
        "default='1'/>"
208
24
        "   <Option name='S57_INTU' type='int' description='Intended usage' "
209
24
        "default='4'/>"
210
24
        "   <Option name='S57_EDTN' type='string' description='Edition number' "
211
24
        "default='2'/>"
212
24
        "   <Option name='S57_UPDN' type='string' description='Update number' "
213
24
        "default='0'/>"
214
24
        "   <Option name='S57_UADT' type='string' description='Update "
215
24
        "application date' default='20030801'/>"
216
24
        "   <Option name='S57_ISDT' type='string' description='Issue date' "
217
24
        "default='20030801'/>"
218
24
        "   <Option name='S57_STED' type='string' description='Edition number "
219
24
        "of S-57' default='03.1'/>"
220
24
        "   <Option name='S57_AGEN' type='int' description='Producing agency' "
221
24
        "default='540'/>"
222
24
        "   <Option name='S57_COMT' type='string' description='Comment' "
223
24
        "default=''/>"
224
24
        "   <Option name='S57_AALL' type='int' description='Lexical level used "
225
24
        "for the ATTF fields' default='0'/>"
226
24
        "   <Option name='S57_NALL' type='int' description='Lexical level used "
227
24
        "for the NATF fields' default='0'/>"
228
24
        "   <Option name='S57_NOMR' type='int' description='Number of meta "
229
24
        "records (objects with acronym starting with \"M_\")' default='0'/>"
230
24
        "   <Option name='S57_NOGR' type='int' description='Number of geo "
231
24
        "records' default='0'/>"
232
24
        "   <Option name='S57_NOLR' type='int' description='Number of "
233
24
        "collection records' default='0'/>"
234
24
        "   <Option name='S57_NOIN' type='int' description='Number of isolated "
235
24
        "node records' default='0'/>"
236
24
        "   <Option name='S57_NOCN' type='int' description='Number of "
237
24
        "connected node records' default='0'/>"
238
24
        "   <Option name='S57_NOED' type='int' description='Number of edge "
239
24
        "records' default='0'/>"
240
24
        "   <Option name='S57_HDAT' type='int' description='Horizontal "
241
24
        "geodetic datum' default='2'/>"
242
24
        "   <Option name='S57_VDAT' type='int' description='Vertical datum' "
243
24
        "default='17'/>"
244
24
        "   <Option name='S57_SDAT' type='int' description='Sounding datum' "
245
24
        "default='23'/>"
246
24
        "   <Option name='S57_CSCL' type='int' description='Compilation scale "
247
24
        "of data (1:X)' default='52000'/>"
248
24
        "   <Option name='S57_COMF' type='int' description='Floating-point to "
249
24
        "integer multiplication factor for coordinate values' "
250
24
        "default='10000000'/>"
251
24
        "   <Option name='S57_SOMF' type='int' description='Floating point to "
252
24
        "integer multiplication factor for 3-D (sounding) values' "
253
24
        "default='10'/>"
254
24
        "</CreationOptionList>");
255
256
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
257
24
    poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
258
24
    poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
259
260
24
    poDriver->pfnOpen = OGRS57Driver::Open;
261
24
    poDriver->pfnIdentify = OGRS57DriverIdentify;
262
24
    poDriver->pfnCreate = OGRS57Driver::Create;
263
264
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
265
24
}