Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/s57/ogrs57driver.cpp
Line
Count
Source
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
22
{
27
22
}
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
173k
{
56
173k
    if (poOpenInfo->nHeaderBytes < 10)
57
55.5k
        return false;
58
117k
    const char *pachLeader = reinterpret_cast<char *>(poOpenInfo->pabyHeader);
59
117k
    if ((pachLeader[5] != '1' && pachLeader[5] != '2' &&
60
109k
         pachLeader[5] != '3') ||
61
8.76k
        pachLeader[6] != 'L' || (pachLeader[8] != '1' && pachLeader[8] != ' '))
62
116k
    {
63
116k
        return false;
64
116k
    }
65
    // Test for S-57 DSID field structure (to distinguish it from S-101)
66
704
    return strstr(pachLeader, "DSID") != nullptr &&
67
668
           (strstr(pachLeader,
68
668
                   "RCNM!RCID!EXPP!INTU!DSNM!EDTN!UPDN!UADT!ISDT!"
69
668
                   "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
16
            strstr(pachLeader, "RCNM!RCID!EXPP!xxxx") != nullptr);
72
117k
}
73
74
/************************************************************************/
75
/*                                Open()                                */
76
/************************************************************************/
77
78
GDALDataset *OGRS57Driver::Open(GDALOpenInfo *poOpenInfo)
79
80
327
{
81
327
    if (!OGRS57DriverIdentify(poOpenInfo))
82
0
        return nullptr;
83
84
327
    OGRS57DataSource *poDS = new OGRS57DataSource(poOpenInfo->papszOpenOptions);
85
327
    if (!poDS->Open(poOpenInfo->pszFilename))
86
327
    {
87
327
        delete poDS;
88
327
        poDS = nullptr;
89
327
    }
90
91
327
    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
327
    return poDS;
100
327
}
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
705
{
126
    /* -------------------------------------------------------------------- */
127
    /*      Instantiate the class registrar if possible.                    */
128
    /* -------------------------------------------------------------------- */
129
705
    CPLMutexHolderD(&hS57RegistrarMutex);
130
131
705
    if (poRegistrar == nullptr)
132
2
    {
133
2
        poRegistrar = new S57ClassRegistrar();
134
135
2
        if (!poRegistrar->LoadInfo(nullptr, nullptr, false))
136
0
        {
137
0
            delete poRegistrar;
138
0
            poRegistrar = nullptr;
139
0
        }
140
2
    }
141
142
705
    return poRegistrar;
143
705
}
144
145
/************************************************************************/
146
/*                           RegisterOGRS57()                           */
147
/************************************************************************/
148
149
void RegisterOGRS57()
150
151
22
{
152
22
    if (GDALGetDriverByName("S57") != nullptr)
153
0
        return;
154
155
22
    GDALDriver *poDriver = new OGRS57Driver();
156
157
22
    poDriver->SetDescription("S57");
158
22
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
159
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "IHO S-57 (ENC)");
160
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "000");
161
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/s57.html");
162
22
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
163
164
22
    poDriver->SetMetadataItem(
165
22
        GDAL_DMD_OPENOPTIONLIST,
166
22
        "<OpenOptionList>"
167
22
        "  <Option name='" S57O_UPDATES
168
22
        "' type='string-select' description='Should update files be "
169
22
        "incorporated into the base data on the fly' default='APPLY'>"
170
22
        "    <Value>APPLY</Value>"
171
22
        "    <Value>IGNORE</Value>"
172
22
        "  </Option>"
173
22
        "  <Option name='" S57O_SPLIT_MULTIPOINT
174
22
        "' type='boolean' description='Should multipoint soundings be split "
175
22
        "into many single point sounding features' default='NO'/>"
176
22
        "  <Option name='" S57O_ADD_SOUNDG_DEPTH
177
22
        "' type='boolean' description='Should a DEPTH attribute be added on "
178
22
        "SOUNDG features and assign the depth of the sounding' default='NO'/>"
179
22
        "  <Option name='" S57O_RETURN_PRIMITIVES
180
22
        "' type='boolean' description='Should all the low level geometry "
181
22
        "primitives be returned as special IsolatedNode, ConnectedNode, Edge "
182
22
        "and Face layers' default='NO'/>"
183
22
        "  <Option name='" S57O_PRESERVE_EMPTY_NUMBERS
184
22
        "' type='boolean' description='If enabled, numeric attributes assigned "
185
22
        "an empty string as a value will be preserved as a special numeric "
186
22
        "value' default='NO'/>"
187
22
        "  <Option name='" S57O_LNAM_REFS
188
22
        "' type='boolean' description='Should LNAM and LNAM_REFS fields be "
189
22
        "attached to features capturing the feature to feature relationships "
190
22
        "in the FFPT group of the S-57 file' default='NO'/>"
191
22
        "  <Option name='" S57O_RETURN_LINKAGES
192
22
        "' type='boolean' description='Should additional attributes relating "
193
22
        "features to their underlying geometric primitives be attached' "
194
22
        "default='NO'/>"
195
22
        "  <Option name='" S57O_RECODE_BY_DSSI
196
22
        "' type='boolean' description='Should attribute values be recoded to "
197
22
        "UTF-8 from the character encoding specified in the S57 DSSI record.' "
198
22
        "default='YES'/>"
199
22
        "  <Option name='" S57O_LIST_AS_STRING
200
22
        "' type='boolean' description='Whether attributes tagged as list in "
201
22
        "S57 dictionaries should be reported as a String field' default='NO'/>"
202
22
        "</OpenOptionList>");
203
22
    poDriver->SetMetadataItem(
204
22
        GDAL_DMD_CREATIONOPTIONLIST,
205
22
        "<CreationOptionList>"
206
22
        "   <Option name='S57_EXPP' type='int' description='Exchange purpose' "
207
22
        "default='1'/>"
208
22
        "   <Option name='S57_INTU' type='int' description='Intended usage' "
209
22
        "default='4'/>"
210
22
        "   <Option name='S57_EDTN' type='string' description='Edition number' "
211
22
        "default='2'/>"
212
22
        "   <Option name='S57_UPDN' type='string' description='Update number' "
213
22
        "default='0'/>"
214
22
        "   <Option name='S57_UADT' type='string' description='Update "
215
22
        "application date' default='20030801'/>"
216
22
        "   <Option name='S57_ISDT' type='string' description='Issue date' "
217
22
        "default='20030801'/>"
218
22
        "   <Option name='S57_STED' type='string' description='Edition number "
219
22
        "of S-57' default='03.1'/>"
220
22
        "   <Option name='S57_AGEN' type='int' description='Producing agency' "
221
22
        "default='540'/>"
222
22
        "   <Option name='S57_COMT' type='string' description='Comment' "
223
22
        "default=''/>"
224
22
        "   <Option name='S57_AALL' type='int' description='Lexical level used "
225
22
        "for the ATTF fields' default='0'/>"
226
22
        "   <Option name='S57_NALL' type='int' description='Lexical level used "
227
22
        "for the NATF fields' default='0'/>"
228
22
        "   <Option name='S57_NOMR' type='int' description='Number of meta "
229
22
        "records (objects with acronym starting with \"M_\")' default='0'/>"
230
22
        "   <Option name='S57_NOGR' type='int' description='Number of geo "
231
22
        "records' default='0'/>"
232
22
        "   <Option name='S57_NOLR' type='int' description='Number of "
233
22
        "collection records' default='0'/>"
234
22
        "   <Option name='S57_NOIN' type='int' description='Number of isolated "
235
22
        "node records' default='0'/>"
236
22
        "   <Option name='S57_NOCN' type='int' description='Number of "
237
22
        "connected node records' default='0'/>"
238
22
        "   <Option name='S57_NOED' type='int' description='Number of edge "
239
22
        "records' default='0'/>"
240
22
        "   <Option name='S57_HDAT' type='int' description='Horizontal "
241
22
        "geodetic datum' default='2'/>"
242
22
        "   <Option name='S57_VDAT' type='int' description='Vertical datum' "
243
22
        "default='17'/>"
244
22
        "   <Option name='S57_SDAT' type='int' description='Sounding datum' "
245
22
        "default='23'/>"
246
22
        "   <Option name='S57_CSCL' type='int' description='Compilation scale "
247
22
        "of data (1:X)' default='52000'/>"
248
22
        "   <Option name='S57_COMF' type='int' description='Floating-point to "
249
22
        "integer multiplication factor for coordinate values' "
250
22
        "default='10000000'/>"
251
22
        "   <Option name='S57_SOMF' type='int' description='Floating point to "
252
22
        "integer multiplication factor for 3-D (sounding) values' "
253
22
        "default='10'/>"
254
22
        "</CreationOptionList>");
255
256
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
257
22
    poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
258
22
    poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
259
260
22
    poDriver->pfnOpen = OGRS57Driver::Open;
261
22
    poDriver->pfnIdentify = OGRS57DriverIdentify;
262
22
    poDriver->pfnCreate = OGRS57Driver::Create;
263
264
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
265
22
}