Coverage Report

Created: 2025-08-11 09:23

/src/gdal/frmts/netcdf/netcdflayersg.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  netCDF read/write Driver
4
 * Purpose:  GDAL bindings over netCDF library.
5
 * Author:   Winor Chen <wchen329 at wisc.edu>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2019, Winor Chen <wchen329 at wisc.edu>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
#include "netcdfsg.h"
13
#include "netcdfdataset.h"
14
#include "ogr_core.h"
15
16
namespace nccfdriver
17
{
18
OGRwkbGeometryType RawToOGR(geom_t type, int axis_count)
19
0
{
20
0
    OGRwkbGeometryType ret = wkbNone;
21
22
0
    switch (type)
23
0
    {
24
0
        case NONE:
25
0
            break;
26
0
        case LINE:
27
0
            ret = axis_count == 2   ? wkbLineString
28
0
                  : axis_count == 3 ? wkbLineString25D
29
0
                                    : wkbNone;
30
0
            break;
31
0
        case MULTILINE:
32
0
            ret = axis_count == 2   ? wkbMultiLineString
33
0
                  : axis_count == 3 ? wkbMultiLineString25D
34
0
                                    : wkbNone;
35
0
            break;
36
0
        case POLYGON:
37
0
            ret = axis_count == 2   ? wkbPolygon
38
0
                  : axis_count == 3 ? wkbPolygon25D
39
0
                                    : wkbNone;
40
0
            break;
41
0
        case MULTIPOLYGON:
42
0
            ret = axis_count == 2   ? wkbMultiPolygon
43
0
                  : axis_count == 3 ? wkbMultiPolygon25D
44
0
                                    : wkbNone;
45
0
            break;
46
0
        case POINT:
47
0
            ret = axis_count == 2   ? wkbPoint
48
0
                  : axis_count == 3 ? wkbPoint25D
49
0
                                    : wkbNone;
50
0
            break;
51
0
        case MULTIPOINT:
52
0
            ret = axis_count == 2   ? wkbMultiPoint
53
0
                  : axis_count == 3 ? wkbMultiPoint25D
54
0
                                    : wkbNone;
55
0
            break;
56
0
        case UNSUPPORTED:
57
0
            break;
58
0
    }
59
60
0
    return ret;
61
0
}
62
63
geom_t OGRtoRaw(OGRwkbGeometryType type)
64
2.94k
{
65
2.94k
    geom_t ret = NONE;
66
2.94k
    auto eFlatType = wkbFlatten(type);
67
68
2.94k
    if (eFlatType == wkbPoint)
69
0
    {
70
0
        ret = POINT;
71
0
    }
72
73
2.94k
    else if (eFlatType == wkbLineString)
74
0
    {
75
0
        ret = LINE;
76
0
    }
77
78
2.94k
    else if (eFlatType == wkbPolygon)
79
0
    {
80
0
        ret = POLYGON;
81
0
    }
82
83
2.94k
    else if (eFlatType == wkbMultiPoint)
84
0
    {
85
0
        ret = MULTIPOINT;
86
0
    }
87
88
2.94k
    else if (eFlatType == wkbMultiLineString)
89
0
    {
90
0
        ret = MULTILINE;
91
0
    }
92
93
2.94k
    else if (eFlatType == wkbMultiPolygon)
94
0
    {
95
0
        ret = MULTIPOLYGON;
96
0
    }
97
98
    // if the feature type isn't NONE potentially give a warning about measures
99
2.94k
    if (ret != NONE && wkbHasM(type))
100
0
    {
101
0
        CPLError(CE_Warning, CPLE_NotSupported,
102
0
                 "A partially supported measured feature type was detected. X, "
103
0
                 "Y, Z Geometry will be preserved but the measure axis and "
104
0
                 "related information will be removed.");
105
0
    }
106
107
2.94k
    return ret;
108
2.94k
}
109
110
bool OGRHasZandSupported(OGRwkbGeometryType type)
111
0
{
112
0
    return type == wkbPoint25D || type == wkbLineString25D ||
113
0
           type == wkbPolygon25D || type == wkbMultiPoint25D ||
114
0
           type == wkbMultiLineString25D || type == wkbMultiPolygon25D;
115
0
}
116
117
}  // namespace nccfdriver
118
119
bool netCDFDataset::DetectAndFillSGLayers(int ncid)
120
35
{
121
    // Discover simple geometry variables
122
35
    int var_count;
123
35
    nc_inq_nvars(ncid, &var_count);
124
35
    std::set<int> vidList;
125
126
35
    nccfdriver::scanForGeometryContainers(ncid, vidList);
127
128
35
    if (!vidList.empty())
129
0
    {
130
0
        for (auto vid : vidList)
131
0
        {
132
0
            try
133
0
            {
134
0
                LoadSGVarIntoLayer(ncid, vid);
135
0
            }
136
137
0
            catch (nccfdriver::SG_Exception &e)
138
0
            {
139
0
                CPLError(CE_Warning, CPLE_AppDefined,
140
0
                         "Translation of a simple geometry layer has been "
141
0
                         "terminated prematurely due to an error.\n%s",
142
0
                         e.get_err_msg());
143
0
            }
144
0
        }
145
0
    }
146
147
35
    return !vidList.empty();
148
35
}
149
150
CPLErr netCDFDataset::LoadSGVarIntoLayer(int ncid, int nc_basevarId)
151
0
{
152
0
    std::shared_ptr<nccfdriver::SGeometry_Reader> sg(
153
0
        new nccfdriver::SGeometry_Reader(ncid, nc_basevarId));
154
0
    int cont_id = sg->getContainerId();
155
0
    nccfdriver::SGeometry_PropertyScanner pr(ncid, cont_id);
156
0
    OGRwkbGeometryType owgt =
157
0
        nccfdriver::RawToOGR(sg->getGeometryType(), sg->get_axisCount());
158
159
0
    std::string return_gm = "";
160
161
0
    if (sg->getGridMappingVarID() != nccfdriver::INVALID_VAR_ID)
162
0
        SetProjectionFromVar(ncid, nc_basevarId, true,
163
0
                             sg->getGridMappingName().c_str(), &return_gm,
164
0
                             sg.get(), /*paosRemovedMDItems=*/nullptr);
165
166
    // Geometry Type invalid, avoid further processing
167
0
    if (owgt == wkbNone)
168
0
    {
169
0
        throw nccfdriver::SG_Exception_BadFeature();
170
0
    }
171
172
0
    char baseName[NC_MAX_CHAR + 1];
173
0
    memset(baseName, 0, NC_MAX_CHAR + 1);
174
0
    nc_inq_varname(ncid, nc_basevarId, baseName);
175
176
0
    OGRSpatialReference *poSRS = nullptr;
177
0
    if (return_gm != "")
178
0
    {
179
0
        poSRS = new OGRSpatialReference();
180
0
        if (poSRS->importFromWkt(return_gm.c_str()) != OGRERR_NONE)
181
0
        {
182
0
            delete poSRS;
183
0
            throw nccfdriver::SG_Exception_General_Malformed("SRS settings");
184
0
        }
185
186
0
        poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
187
0
    }
188
189
0
    std::shared_ptr<netCDFLayer> poL(
190
0
        new netCDFLayer(this, ncid, baseName, owgt, poSRS));
191
192
0
    if (poSRS != nullptr)
193
0
    {
194
0
        poSRS->Release();
195
0
    }
196
197
0
    poL->EnableSGBypass();
198
0
    OGRFeatureDefn *defn = poL->GetLayerDefn();
199
0
    defn->SetGeomType(owgt);
200
201
    // Add properties
202
0
    std::vector<int> props = pr.ids();
203
0
    for (size_t itr = 0; itr < props.size(); itr++)
204
0
    {
205
0
        poL->AddField(props[itr]);
206
0
    }
207
208
    // Set simple geometry object
209
0
    poL->SetSGeometryRepresentation(sg);
210
211
    // Create layer
212
0
    papoLayers.push_back(poL);
213
214
0
    return CE_None;
215
0
}
216
217
/* Creates and fills any needed variables that haven't already been created
218
 */
219
bool netCDFDataset::SGCommitPendingTransaction()
220
0
{
221
0
    try
222
0
    {
223
0
        if (bSGSupport)
224
0
        {
225
            // Go through all the layers and resize dimensions accordingly
226
0
            for (size_t layerInd = 0; layerInd < papoLayers.size(); layerInd++)
227
0
            {
228
0
                auto poLayer =
229
0
                    dynamic_cast<netCDFLayer *>(papoLayers[layerInd].get());
230
0
                if (!poLayer)
231
0
                    continue;
232
0
                nccfdriver::ncLayer_SG_Metadata &layerMD =
233
0
                    poLayer->getLayerSGMetadata();
234
0
                nccfdriver::geom_t wType = layerMD.getWritableType();
235
236
                // Resize node coordinates
237
0
                int ncoord_did = layerMD.get_node_coord_dimID();
238
0
                if (ncoord_did != nccfdriver::INVALID_DIM_ID)
239
0
                {
240
0
                    vcdf.nc_resize_vdim(
241
0
                        ncoord_did, layerMD.get_next_write_pos_node_coord());
242
0
                }
243
244
                // Resize node count (for all except POINT)
245
0
                if (wType != nccfdriver::POINT)
246
0
                {
247
0
                    int ncount_did = layerMD.get_node_count_dimID();
248
0
                    if (ncount_did != nccfdriver::INVALID_DIM_ID)
249
0
                    {
250
0
                        vcdf.nc_resize_vdim(
251
0
                            ncount_did,
252
0
                            layerMD.get_next_write_pos_node_count());
253
0
                    }
254
0
                }
255
256
                // Resize part node count (for MULTILINE, POLYGON, MULTIPOLYGON)
257
0
                if (wType == nccfdriver::MULTILINE ||
258
0
                    wType == nccfdriver::POLYGON ||
259
0
                    wType == nccfdriver::MULTIPOLYGON)
260
0
                {
261
0
                    int pnc_did = layerMD.get_pnc_dimID();
262
0
                    if (pnc_did != nccfdriver::INVALID_DIM_ID)
263
0
                    {
264
0
                        vcdf.nc_resize_vdim(pnc_did,
265
0
                                            layerMD.get_next_write_pos_pnc());
266
0
                    }
267
0
                }
268
269
0
                nccfdriver::geom_t geometry_type = layerMD.getWritableType();
270
271
                /* Delete interior ring stuff if not detected
272
                 */
273
274
0
                if (!layerMD.getInteriorRingDetected() &&
275
0
                    (geometry_type == nccfdriver::MULTIPOLYGON ||
276
0
                     geometry_type == nccfdriver::POLYGON) &&
277
0
                    layerMD.get_containerRealID() != nccfdriver::INVALID_VAR_ID)
278
0
                {
279
0
                    SetDefineMode(true);
280
281
0
                    int err_code =
282
0
                        nc_del_att(cdfid, layerMD.get_containerRealID(),
283
0
                                   CF_SG_INTERIOR_RING);
284
0
                    NCDF_ERR(err_code);
285
0
                    if (err_code != NC_NOERR)
286
0
                    {
287
0
                        std::string frmt = std::string("attribute: ") +
288
0
                                           std::string(CF_SG_INTERIOR_RING);
289
0
                        throw nccfdriver::SGWriter_Exception_NCDelFailure(
290
0
                            layerMD.get_containerName().c_str(), frmt.c_str());
291
0
                    }
292
293
                    // Invalidate variable writes as well - Interior Ring
294
0
                    vcdf.nc_del_vvar(layerMD.get_intring_varID());
295
296
0
                    if (geometry_type == nccfdriver::POLYGON)
297
0
                    {
298
0
                        err_code =
299
0
                            nc_del_att(cdfid, layerMD.get_containerRealID(),
300
0
                                       CF_SG_PART_NODE_COUNT);
301
0
                        NCDF_ERR(err_code);
302
0
                        if (err_code != NC_NOERR)
303
0
                        {
304
0
                            std::string frmt =
305
0
                                std::string("attribute: ") +
306
0
                                std::string(CF_SG_PART_NODE_COUNT);
307
0
                            throw nccfdriver::SGWriter_Exception_NCDelFailure(
308
0
                                layerMD.get_containerName().c_str(),
309
0
                                frmt.c_str());
310
0
                        }
311
312
                        // Invalidate variable writes as well - Part Node Count
313
0
                        vcdf.nc_del_vvar(layerMD.get_pnc_varID());
314
315
                        // Invalidate dimension as well - Part Node Count
316
0
                        vcdf.nc_del_vdim(layerMD.get_pnc_dimID());
317
0
                    }
318
319
0
                    SetDefineMode(false);
320
0
                }
321
0
            }
322
323
0
            vcdf.nc_vmap();
324
0
            this->FieldScribe.commit_transaction();
325
0
            this->GeometryScribe.commit_transaction();
326
0
        }
327
0
    }
328
329
0
    catch (nccfdriver::SG_Exception &sge)
330
0
    {
331
0
        CPLError(CE_Fatal, CPLE_FileIO,
332
0
                 "An error occurred while writing the target netCDF File. "
333
0
                 "Translation will be terminated.\n%s",
334
0
                 sge.get_err_msg());
335
0
        return false;
336
0
    }
337
0
    return true;
338
0
}
339
340
void netCDFDataset::SGLogPendingTransaction()
341
0
{
342
0
    GeometryScribe.log_transaction();
343
0
    FieldScribe.log_transaction();
344
0
}
345
346
/* Takes an index and using the layer geometry builds the equivalent
347
 * OGRFeature.
348
 */
349
OGRFeature *netCDFLayer::buildSGeometryFeature(size_t featureInd)
350
0
{
351
0
    OGRGeometry *geometry;
352
353
0
    switch (m_simpleGeometryReader->getGeometryType())
354
0
    {
355
0
        case nccfdriver::POINT:
356
0
            geometry = new OGRPoint;
357
0
            break;
358
0
        case nccfdriver::LINE:
359
0
            geometry = new OGRLineString;
360
0
            break;
361
0
        case nccfdriver::POLYGON:
362
0
            geometry = new OGRPolygon;
363
0
            break;
364
0
        case nccfdriver::MULTIPOINT:
365
0
            geometry = new OGRMultiPoint;
366
0
            break;
367
0
        case nccfdriver::MULTILINE:
368
0
            geometry = new OGRMultiLineString;
369
0
            break;
370
0
        case nccfdriver::MULTIPOLYGON:
371
0
            geometry = new OGRMultiPolygon;
372
0
            break;
373
0
        default:
374
0
            throw nccfdriver::SG_Exception_BadFeature();
375
0
            break;
376
0
    }
377
378
0
    const auto wkb = m_simpleGeometryReader->serializeToWKB(featureInd);
379
0
    geometry->importFromWkb(wkb.data(), wkb.size(), wkbVariantIso);
380
0
    geometry->assignSpatialReference(this->GetSpatialRef());
381
382
0
    OGRFeatureDefn *defn = this->GetLayerDefn();
383
0
    OGRFeature *feat = new OGRFeature(defn);
384
0
    feat->SetGeometryDirectly(geometry);
385
386
0
    int dimId = m_simpleGeometryReader->getInstDim();
387
388
0
    this->FillFeatureFromVar(feat, dimId, featureInd);
389
390
0
    feat->SetFID(featureInd);
391
0
    return feat;
392
0
}
393
394
std::string netCDFDataset::generateLogName()
395
46.7k
{
396
46.7k
    return std::string(CPLGenerateTempFilenameSafe(nullptr));
397
46.7k
}