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/dgn/dgnopen.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Microstation DGN Access Library
4
 * Purpose:  DGN Access Library file open code.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "dgnlibp.h"
14
15
/************************************************************************/
16
/*                            DGNTestOpen()                             */
17
/************************************************************************/
18
19
/**
20
 * Test if header is DGN.
21
 *
22
 * @param pabyHeader block of header data from beginning of file.
23
 * @param nByteCount number of bytes in pabyHeader.
24
 *
25
 * @return TRUE if the header appears to be from a DGN file, otherwise FALSE.
26
 */
27
28
int DGNTestOpen(GByte *pabyHeader, int nByteCount)
29
30
103k
{
31
103k
    if (nByteCount < 4)
32
0
        return FALSE;
33
34
    // Is it a cell library?
35
103k
    if (pabyHeader[0] == 0x08 && pabyHeader[1] == 0x05 &&
36
7.39k
        pabyHeader[2] == 0x17 && pabyHeader[3] == 0x00)
37
7.38k
        return TRUE;
38
39
    // Is it not a regular 2D or 3D file?
40
95.9k
    if ((pabyHeader[0] != 0x08 && pabyHeader[0] != 0xC8) ||
41
387
        pabyHeader[1] != 0x09 || pabyHeader[2] != 0xFE || pabyHeader[3] != 0x02)
42
95.6k
        return FALSE;
43
44
333
    return TRUE;
45
95.9k
}
46
47
/************************************************************************/
48
/*                              DGNOpen()                               */
49
/************************************************************************/
50
51
/**
52
 * Open a DGN file.
53
 *
54
 * The file is opened, and minimally verified to ensure it is a DGN (ISFF)
55
 * file.  If the file cannot be opened for read access an error with code
56
 * CPLE_OpenFailed with be reported via CPLError() and NULL returned.
57
 * If the file header does
58
 * not appear to be a DGN file, an error with code CPLE_AppDefined will be
59
 * reported via CPLError(), and NULL returned.
60
 *
61
 * If successful a handle for further access is returned.  This should be
62
 * closed with DGNClose() when no longer needed.
63
 *
64
 * DGNOpen() does not scan the file on open, and should be very fast even for
65
 * large files.
66
 *
67
 * @param pszFilename name of file to try opening.
68
 * @param bUpdate should the file be opened with read+update (r+) mode?
69
 *
70
 * @return handle to use for further access to file using DGN API, or NULL
71
 * if open fails.
72
 */
73
74
DGNHandle DGNOpen(const char *pszFilename, int bUpdate)
75
76
2.58k
{
77
    /* -------------------------------------------------------------------- */
78
    /*      Open the file.                                                  */
79
    /* -------------------------------------------------------------------- */
80
2.58k
    VSILFILE *fp = VSIFOpenL(pszFilename, bUpdate ? "rb+" : "rb");
81
2.58k
    if (fp == nullptr)
82
0
    {
83
0
        CPLError(CE_Failure, CPLE_OpenFailed,
84
0
                 "Unable to open `%s' for read access.\n", pszFilename);
85
0
        return nullptr;
86
0
    }
87
88
    /* -------------------------------------------------------------------- */
89
    /*      Verify the format ... add later.                                */
90
    /* -------------------------------------------------------------------- */
91
2.58k
    GByte abyHeader[512];
92
2.58k
    const int nHeaderBytes =
93
2.58k
        static_cast<int>(VSIFReadL(abyHeader, 1, sizeof(abyHeader), fp));
94
2.58k
    if (!DGNTestOpen(abyHeader, nHeaderBytes))
95
0
    {
96
0
        CPLError(CE_Failure, CPLE_AppDefined,
97
0
                 "File `%s' does not have expected DGN header.\n", pszFilename);
98
0
        VSIFCloseL(fp);
99
0
        return nullptr;
100
0
    }
101
102
2.58k
    VSIRewindL(fp);
103
104
    /* -------------------------------------------------------------------- */
105
    /*      Create the info structure.                                      */
106
    /* -------------------------------------------------------------------- */
107
2.58k
    DGNInfo *psDGN = static_cast<DGNInfo *>(CPLCalloc(sizeof(DGNInfo), 1));
108
2.58k
    psDGN->fp = fp;
109
2.58k
    psDGN->next_element_id = 0;
110
111
2.58k
    psDGN->got_tcb = false;
112
2.58k
    psDGN->scale = 1.0;
113
2.58k
    psDGN->origin_x = 0.0;
114
2.58k
    psDGN->origin_y = 0.0;
115
2.58k
    psDGN->origin_z = 0.0;
116
117
2.58k
    psDGN->index_built = false;
118
2.58k
    psDGN->element_count = 0;
119
2.58k
    psDGN->element_index = nullptr;
120
121
2.58k
    psDGN->got_bounds = false;
122
123
2.58k
    if (abyHeader[0] == 0xC8)
124
0
        psDGN->dimension = 3;
125
2.58k
    else
126
2.58k
        psDGN->dimension = 2;
127
128
2.58k
    psDGN->has_spatial_filter = false;
129
2.58k
    psDGN->sf_converted_to_uor = false;
130
2.58k
    psDGN->select_complex_group = false;
131
2.58k
    psDGN->in_complex_group = false;
132
133
2.58k
    return (DGNHandle)psDGN;
134
2.58k
}
135
136
/************************************************************************/
137
/*                           DGNSetOptions()                            */
138
/************************************************************************/
139
140
/**
141
 * Set file access options.
142
 *
143
 * Sets a flag affecting how the file is accessed.  Currently
144
 * there is only one support flag:
145
 *
146
 * DGNO_CAPTURE_RAW_DATA: If this is enabled (it is off by default),
147
 * then the raw binary data associated with elements will be kept in
148
 * the raw_data field within the DGNElemCore when they are read.  This
149
 * is required if the application needs to interpret the raw data itself.
150
 * It is also necessary if the element is to be written back to this file,
151
 * or another file using DGNWriteElement().  Off by default (to conserve
152
 * memory).
153
 *
154
 * @param hDGN handle to file returned by DGNOpen().
155
 * @param nOptions ORed option flags.
156
 */
157
158
void DGNSetOptions(DGNHandle hDGN, int nOptions)
159
160
9
{
161
9
    DGNInfo *psDGN = (DGNInfo *)hDGN;
162
163
9
    psDGN->options = nOptions;
164
9
}
165
166
/************************************************************************/
167
/*                        DGNSetSpatialFilter()                         */
168
/************************************************************************/
169
170
/**
171
 * Set rectangle for which features are desired.
172
 *
173
 * If a spatial filter is set with this function, DGNReadElement() will
174
 * only return spatial elements (elements with a known bounding box) and
175
 * only those elements for which this bounding box overlaps the requested
176
 * region.
177
 *
178
 * If all four values (dfXMin, dfXMax, dfYMin and dfYMax) are zero, the
179
 * spatial filter is disabled.   Note that installing a spatial filter
180
 * won't reduce the amount of data read from disk.  All elements are still
181
 * scanned, but the amount of processing work for elements outside the
182
 * spatial filter is minimized.
183
 *
184
 * @param hDGN Handle from DGNOpen() for file to update.
185
 * @param dfXMin minimum x coordinate for extents (georeferenced coordinates).
186
 * @param dfYMin minimum y coordinate for extents (georeferenced coordinates).
187
 * @param dfXMax maximum x coordinate for extents (georeferenced coordinates).
188
 * @param dfYMax maximum y coordinate for extents (georeferenced coordinates).
189
 */
190
191
void DGNSetSpatialFilter(DGNHandle hDGN, double dfXMin, double dfYMin,
192
                         double dfXMax, double dfYMax)
193
194
0
{
195
0
    DGNInfo *psDGN = (DGNInfo *)hDGN;
196
197
0
    if (dfXMin == 0.0 && dfXMax == 0.0 && dfYMin == 0.0 && dfYMax == 0.0)
198
0
    {
199
0
        psDGN->has_spatial_filter = false;
200
0
        return;
201
0
    }
202
203
0
    psDGN->has_spatial_filter = true;
204
0
    psDGN->sf_converted_to_uor = false;
205
206
0
    psDGN->sf_min_x_geo = dfXMin;
207
0
    psDGN->sf_min_y_geo = dfYMin;
208
0
    psDGN->sf_max_x_geo = dfXMax;
209
0
    psDGN->sf_max_y_geo = dfYMax;
210
211
0
    DGNSpatialFilterToUOR(psDGN);
212
0
}
213
214
/************************************************************************/
215
/*                       DGNSpatialFilterToUOR()                        */
216
/************************************************************************/
217
218
void DGNSpatialFilterToUOR(DGNInfo *psDGN)
219
220
0
{
221
0
    if (psDGN->sf_converted_to_uor || !psDGN->has_spatial_filter ||
222
0
        !psDGN->got_tcb)
223
0
        return;
224
225
0
    DGNPoint sMin = {psDGN->sf_min_x_geo, psDGN->sf_min_y_geo, 0};
226
227
0
    DGNPoint sMax = {psDGN->sf_max_x_geo, psDGN->sf_max_y_geo, 0};
228
229
0
    DGNInverseTransformPoint(psDGN, &sMin);
230
0
    DGNInverseTransformPoint(psDGN, &sMax);
231
232
0
    psDGN->sf_min_x = (GUInt32)(sMin.x + 2147483648.0);
233
0
    psDGN->sf_min_y = (GUInt32)(sMin.y + 2147483648.0);
234
0
    psDGN->sf_max_x = (GUInt32)(sMax.x + 2147483648.0);
235
0
    psDGN->sf_max_y = (GUInt32)(sMax.y + 2147483648.0);
236
237
0
    psDGN->sf_converted_to_uor = true;
238
0
}
239
240
/************************************************************************/
241
/*                              DGNClose()                              */
242
/************************************************************************/
243
244
/**
245
 * Close DGN file.
246
 *
247
 * @param hDGN Handle from DGNOpen() for file to close.
248
 */
249
250
void DGNClose(DGNHandle hDGN)
251
252
2.58k
{
253
2.58k
    DGNInfo *psDGN = (DGNInfo *)hDGN;
254
255
2.58k
    VSIFCloseL(psDGN->fp);
256
2.58k
    CPLFree(psDGN->element_index);
257
2.58k
    CPLFree(psDGN);
258
2.58k
}
259
260
/************************************************************************/
261
/*                          DGNGetDimension()                           */
262
/************************************************************************/
263
264
/**
265
 * Return 2D/3D dimension of file.
266
 *
267
 * Return 2 or 3 depending on the dimension value of the provided file.
268
 */
269
270
int DGNGetDimension(DGNHandle hDGN)
271
272
349k
{
273
349k
    DGNInfo *psDGN = (DGNInfo *)hDGN;
274
275
349k
    return psDGN->dimension;
276
349k
}