Coverage Report

Created: 2025-12-31 08:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/nas/ogrnasdatasource.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OGR
4
 * Purpose:  Implements OGRNASDataSource class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_conv.h"
15
#include "cpl_string.h"
16
#include "ogr_nas.h"
17
18
static const char *const apszURNNames[] = {
19
    "DE_DHDN_3GK2_*", "EPSG:31466", "DE_DHDN_3GK3_*", "EPSG:31467",
20
    "ETRS89_UTM32",   "EPSG:25832", "ETRS89_UTM33",   "EPSG:25833",
21
    nullptr,          nullptr};
22
23
/************************************************************************/
24
/*                         OGRNASDataSource()                           */
25
/************************************************************************/
26
27
OGRNASDataSource::OGRNASDataSource()
28
0
    : papoLayers(nullptr), nLayers(0), poReader(nullptr)
29
0
{
30
0
}
31
32
/************************************************************************/
33
/*                        ~OGRNASDataSource()                         */
34
/************************************************************************/
35
36
OGRNASDataSource::~OGRNASDataSource()
37
38
0
{
39
0
    for (int i = 0; i < nLayers; i++)
40
0
        delete papoLayers[i];
41
42
0
    CPLFree(papoLayers);
43
44
0
    if (poReader)
45
0
        delete poReader;
46
0
}
47
48
/************************************************************************/
49
/*                                Open()                                */
50
/************************************************************************/
51
52
int OGRNASDataSource::Open(const char *pszNewName)
53
54
0
{
55
0
    poReader = CreateNASReader();
56
0
    if (poReader == nullptr)
57
0
    {
58
0
        CPLError(CE_Failure, CPLE_AppDefined,
59
0
                 "File %s appears to be NAS but the NAS reader cannot\n"
60
0
                 "be instantiated, likely because Xerces support was not\n"
61
0
                 "configured in.",
62
0
                 pszNewName);
63
0
        return FALSE;
64
0
    }
65
66
0
    poReader->SetSourceFile(pszNewName);
67
68
0
    bool bHaveSchema = false;
69
0
    bool bHaveTemplate = false;
70
71
    // Is some NAS Feature Schema (.gfs) TEMPLATE required?
72
0
    const char *pszNASTemplateName = CPLGetConfigOption("NAS_GFS_TEMPLATE", "");
73
0
    if (!EQUAL(pszNASTemplateName, ""))
74
0
    {
75
        // Load the TEMPLATE.
76
0
        if (!poReader->LoadClasses(pszNASTemplateName))
77
0
        {
78
0
            CPLError(CE_Failure, CPLE_AppDefined,
79
0
                     "NAS schema %s could not be loaded\n", pszNASTemplateName);
80
0
            return FALSE;
81
0
        }
82
83
0
        bHaveTemplate = true;
84
85
0
        CPLDebug("NAS", "Schema loaded.");
86
0
    }
87
0
    else
88
0
    {
89
        /* --------------------------------------------------------------------
90
         */
91
        /*      Can we find a NAS Feature Schema (.gfs) for the input file? */
92
        /* --------------------------------------------------------------------
93
         */
94
0
        const std::string osGFSFilename =
95
0
            CPLResetExtensionSafe(pszNewName, "gfs");
96
0
        VSIStatBufL sGFSStatBuf;
97
0
        if (VSIStatL(osGFSFilename.c_str(), &sGFSStatBuf) == 0)
98
0
        {
99
0
            VSIStatBufL sNASStatBuf;
100
0
            if (VSIStatL(pszNewName, &sNASStatBuf) == 0 &&
101
0
                sNASStatBuf.st_mtime > sGFSStatBuf.st_mtime)
102
0
            {
103
0
                CPLDebug("NAS",
104
0
                         "Found %s but ignoring because it appears "
105
0
                         "be older than the associated NAS file.",
106
0
                         osGFSFilename.c_str());
107
0
            }
108
0
            else
109
0
            {
110
0
                bHaveSchema = poReader->LoadClasses(osGFSFilename.c_str());
111
0
            }
112
0
        }
113
114
0
        if (!bHaveSchema)
115
0
        {
116
0
            CPLError(CE_Failure, CPLE_AppDefined,
117
0
                     "No schema information loaded");
118
0
        }
119
0
    }
120
121
    /* -------------------------------------------------------------------- */
122
    /*      Force a first pass to establish the schema.  The loaded schema  */
123
    /*      if any will be cleaned from any unavailable classes.            */
124
    /* -------------------------------------------------------------------- */
125
0
    CPLErrorReset();
126
0
    if (!bHaveSchema && !poReader->PrescanForSchema(TRUE) &&
127
0
        CPLGetLastErrorType() == CE_Failure)
128
0
    {
129
        // Assume an error has been reported.
130
0
        return FALSE;
131
0
    }
132
133
    /* -------------------------------------------------------------------- */
134
    /*      Save the schema file if possible.  Do not make a fuss if we     */
135
    /*      cannot.  It could be read-only directory or something.          */
136
    /* -------------------------------------------------------------------- */
137
0
    if (!bHaveTemplate && !bHaveSchema && poReader->GetClassCount() > 0 &&
138
0
        !STARTS_WITH_CI(pszNewName, "/vsitar/") &&
139
0
        !STARTS_WITH_CI(pszNewName, "/vsizip/") &&
140
0
        !STARTS_WITH_CI(pszNewName, "/vsigzip/vsi") &&
141
0
        !STARTS_WITH_CI(pszNewName, "/vsigzip//vsi") &&
142
0
        !STARTS_WITH_CI(pszNewName, "/vsicurl/") &&
143
0
        !STARTS_WITH_CI(pszNewName, "/vsicurl_streaming/"))
144
0
    {
145
0
        VSILFILE *fp = nullptr;
146
147
0
        const std::string osGFSFilename =
148
0
            CPLResetExtensionSafe(pszNewName, "gfs");
149
0
        VSIStatBufL sGFSStatBuf;
150
0
        if (VSIStatL(osGFSFilename.c_str(), &sGFSStatBuf) != 0 &&
151
0
            (fp = VSIFOpenL(osGFSFilename.c_str(), "wt")) != nullptr)
152
0
        {
153
0
            VSIFCloseL(fp);
154
0
            poReader->SaveClasses(osGFSFilename.c_str());
155
0
        }
156
0
        else
157
0
        {
158
0
            CPLDebug("NAS",
159
0
                     "Not saving %s. File already exists or can't be created.",
160
0
                     osGFSFilename.c_str());
161
0
        }
162
0
    }
163
164
    /* -------------------------------------------------------------------- */
165
    /*      Translate the GMLFeatureClasses into layers.                    */
166
    /* -------------------------------------------------------------------- */
167
0
    papoLayers = (OGRLayer **)CPLCalloc(sizeof(OGRNASLayer *),
168
0
                                        poReader->GetClassCount() + 1);
169
0
    nLayers = 0;
170
171
0
    while (nLayers < poReader->GetClassCount())
172
0
    {
173
0
        papoLayers[nLayers] = TranslateNASSchema(poReader->GetClass(nLayers));
174
0
        nLayers++;
175
0
    }
176
177
0
    return TRUE;
178
0
}
179
180
/************************************************************************/
181
/*                         TranslateNASSchema()                         */
182
/************************************************************************/
183
184
OGRNASLayer *OGRNASDataSource::TranslateNASSchema(GMLFeatureClass *poClass)
185
186
0
{
187
    /* -------------------------------------------------------------------- */
188
    /*      Translate SRS.                                                  */
189
    /* -------------------------------------------------------------------- */
190
0
    const char *pszSRSName = poClass->GetSRSName();
191
0
    OGRSpatialReference *poSRS = nullptr;
192
0
    if (pszSRSName)
193
0
    {
194
0
        const char *pszHandle = strrchr(pszSRSName, ':');
195
0
        if (pszHandle != nullptr)
196
0
        {
197
0
            pszHandle += 1;
198
199
0
            poSRS = new OGRSpatialReference();
200
0
            poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
201
202
0
            for (int i = 0; apszURNNames[i * 2 + 0] != nullptr; i++)
203
0
            {
204
0
                const char *pszTarget = apszURNNames[i * 2 + 0];
205
0
                const int nTLen = static_cast<int>(strlen(pszTarget));
206
207
                // Are we just looking for a prefix match?
208
0
                if (pszTarget[nTLen - 1] == '*')
209
0
                {
210
0
                    if (EQUALN(pszTarget, pszHandle, nTLen - 1))
211
0
                        pszSRSName = apszURNNames[i * 2 + 1];
212
0
                }
213
0
                else
214
0
                {
215
0
                    if (EQUAL(pszTarget, pszHandle))
216
0
                        pszSRSName = apszURNNames[i * 2 + 1];
217
0
                }
218
0
            }
219
220
0
            if (poSRS->SetFromUserInput(
221
0
                    pszSRSName,
222
0
                    OGRSpatialReference::
223
0
                        SET_FROM_USER_INPUT_LIMITATIONS_get()) != OGRERR_NONE)
224
0
            {
225
0
                CPLDebug("NAS", "Failed to translate srsName='%s'", pszSRSName);
226
0
                delete poSRS;
227
0
                poSRS = nullptr;
228
0
            }
229
0
        }
230
0
    }
231
232
    /* -------------------------------------------------------------------- */
233
    /*      Create an empty layer.                                          */
234
    /* -------------------------------------------------------------------- */
235
0
    OGRNASLayer *poLayer = new OGRNASLayer(poClass->GetName(), this);
236
237
    /* -------------------------------------------------------------------- */
238
    /*      Added attributes (properties).                                  */
239
    /* -------------------------------------------------------------------- */
240
0
    for (int iField = 0; iField < poClass->GetPropertyCount(); iField++)
241
0
    {
242
0
        GMLPropertyDefn *poProperty = poClass->GetProperty(iField);
243
0
        OGRFieldType eFType;
244
245
0
        if (poProperty->GetType() == GMLPT_Untyped)
246
0
            eFType = OFTString;
247
0
        else if (poProperty->GetType() == GMLPT_String)
248
0
            eFType = OFTString;
249
0
        else if (poProperty->GetType() == GMLPT_Integer)
250
0
            eFType = OFTInteger;
251
0
        else if (poProperty->GetType() == GMLPT_Real)
252
0
            eFType = OFTReal;
253
0
        else if (poProperty->GetType() == GMLPT_StringList)
254
0
            eFType = OFTStringList;
255
0
        else if (poProperty->GetType() == GMLPT_IntegerList)
256
0
            eFType = OFTIntegerList;
257
0
        else if (poProperty->GetType() == GMLPT_RealList)
258
0
            eFType = OFTRealList;
259
0
        else
260
0
            eFType = OFTString;
261
262
0
        OGRFieldDefn oField(poProperty->GetName(), eFType);
263
0
        if (STARTS_WITH_CI(oField.GetNameRef(), "ogr:"))
264
0
            oField.SetName(poProperty->GetName() + 4);
265
0
        if (poProperty->GetWidth() > 0)
266
0
            oField.SetWidth(poProperty->GetWidth());
267
268
0
        poLayer->GetLayerDefn()->AddFieldDefn(&oField);
269
0
    }
270
271
0
    for (int iField = 0; iField < poClass->GetGeometryPropertyCount(); iField++)
272
0
    {
273
0
        GMLGeometryPropertyDefn *poProperty =
274
0
            poClass->GetGeometryProperty(iField);
275
0
        OGRGeomFieldDefn oField(poProperty->GetName(),
276
0
                                (OGRwkbGeometryType)poProperty->GetType());
277
0
        if (poClass->GetGeometryPropertyCount() == 1 &&
278
0
            poClass->GetFeatureCount() == 0)
279
0
        {
280
0
            oField.SetType(wkbUnknown);
281
0
        }
282
283
0
        oField.SetSpatialRef(poSRS);
284
0
        oField.SetNullable(poProperty->IsNullable());
285
286
0
        poLayer->GetLayerDefn()->AddGeomFieldDefn(&oField);
287
0
    }
288
289
0
    if (poSRS)
290
0
        poSRS->Dereference();
291
292
0
    return poLayer;
293
0
}
294
295
/************************************************************************/
296
/*                              GetLayer()                              */
297
/************************************************************************/
298
299
const OGRLayer *OGRNASDataSource::GetLayer(int iLayer) const
300
301
0
{
302
0
    if (iLayer < 0 || iLayer >= nLayers)
303
0
        return nullptr;
304
305
0
    return papoLayers[iLayer];
306
0
}