/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 | } |