1 /******************************************************************************
2 * $Id: dgnopen.cpp 27272 2014-05-01 23:14:58Z rouault $
3 *
4 * Project: Microstation DGN Access Library
5 * Purpose: DGN Access Library file open code.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 *
8 ******************************************************************************
9 * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 ****************************************************************************/
29
30 #include "dgnlibp.h"
31
32 CPL_CVSID("$Id: dgnopen.cpp 27272 2014-05-01 23:14:58Z rouault $");
33
34 /************************************************************************/
35 /* DGNTestOpen() */
36 /************************************************************************/
37
38 /**
39 * Test if header is DGN.
40 *
41 * @param pabyHeader block of header data from beginning of file.
42 * @param nByteCount number of bytes in pabyHeader.
43 *
44 * @return TRUE if the header appears to be from a DGN file, otherwise FALSE.
45 */
46
DGNTestOpen(GByte * pabyHeader,int nByteCount)47 int DGNTestOpen( GByte *pabyHeader, int nByteCount )
48
49 {
50 if( nByteCount < 4 )
51 return FALSE;
52
53 // Is it a cell library?
54 if( pabyHeader[0] == 0x08
55 && pabyHeader[1] == 0x05
56 && pabyHeader[2] == 0x17
57 && pabyHeader[3] == 0x00 )
58 return TRUE;
59
60 // Is it not a regular 2D or 3D file?
61 if( (pabyHeader[0] != 0x08 && pabyHeader[0] != 0xC8)
62 || pabyHeader[1] != 0x09
63 || pabyHeader[2] != 0xFE || pabyHeader[3] != 0x02 )
64 return FALSE;
65
66 return TRUE;
67 }
68
69 /************************************************************************/
70 /* DGNOpen() */
71 /************************************************************************/
72
73 /**
74 * Open a DGN file.
75 *
76 * The file is opened, and minimally verified to ensure it is a DGN (ISFF)
77 * file. If the file cannot be opened for read access an error with code
78 * CPLE_OpenFailed with be reported via CPLError() and NULL returned.
79 * If the file header does
80 * not appear to be a DGN file, an error with code CPLE_AppDefined will be
81 * reported via CPLError(), and NULL returned.
82 *
83 * If successful a handle for further access is returned. This should be
84 * closed with DGNClose() when no longer needed.
85 *
86 * DGNOpen() does not scan the file on open, and should be very fast even for
87 * large files.
88 *
89 * @param pszFilename name of file to try opening.
90 * @param bUpdate should the file be opened with read+update (r+) mode?
91 *
92 * @return handle to use for further access to file using DGN API, or NULL
93 * if open fails.
94 */
95
DGNOpen(const char * pszFilename,int bUpdate)96 DGNHandle DGNOpen( const char * pszFilename, int bUpdate )
97
98 {
99 DGNInfo *psDGN;
100 FILE *fp;
101
102 /* -------------------------------------------------------------------- */
103 /* Open the file. */
104 /* -------------------------------------------------------------------- */
105 if( bUpdate )
106 fp = VSIFOpen( pszFilename, "rb+" );
107 else
108 fp = VSIFOpen( pszFilename, "rb" );
109 if( fp == NULL )
110 {
111 CPLError( CE_Failure, CPLE_OpenFailed,
112 "Unable to open `%s' for read access.\n",
113 pszFilename );
114 return NULL;
115 }
116
117 /* -------------------------------------------------------------------- */
118 /* Verify the format ... add later. */
119 /* -------------------------------------------------------------------- */
120 GByte abyHeader[512];
121
122 VSIFRead( abyHeader, 1, sizeof(abyHeader), fp );
123 if( !DGNTestOpen( abyHeader, sizeof(abyHeader) ) )
124 {
125 CPLError( CE_Failure, CPLE_AppDefined,
126 "File `%s' does not have expected DGN header.\n",
127 pszFilename );
128 VSIFClose( fp );
129 return NULL;
130 }
131
132 VSIRewind( fp );
133
134 /* -------------------------------------------------------------------- */
135 /* Create the info structure. */
136 /* -------------------------------------------------------------------- */
137 psDGN = (DGNInfo *) CPLCalloc(sizeof(DGNInfo),1);
138 psDGN->fp = fp;
139 psDGN->next_element_id = 0;
140
141 psDGN->got_tcb = FALSE;
142 psDGN->scale = 1.0;
143 psDGN->origin_x = 0.0;
144 psDGN->origin_y = 0.0;
145 psDGN->origin_z = 0.0;
146
147 psDGN->index_built = FALSE;
148 psDGN->element_count = 0;
149 psDGN->element_index = NULL;
150
151 psDGN->got_bounds = FALSE;
152
153 if( abyHeader[0] == 0xC8 )
154 psDGN->dimension = 3;
155 else
156 psDGN->dimension = 2;
157
158 psDGN->has_spatial_filter = FALSE;
159 psDGN->sf_converted_to_uor = FALSE;
160 psDGN->select_complex_group = FALSE;
161 psDGN->in_complex_group = FALSE;
162
163 return (DGNHandle) psDGN;
164 }
165
166 /************************************************************************/
167 /* DGNSetOptions() */
168 /************************************************************************/
169
170 /**
171 * Set file access options.
172 *
173 * Sets a flag affecting how the file is accessed. Currently
174 * there is only one support flag:
175 *
176 * DGNO_CAPTURE_RAW_DATA: If this is enabled (it is off by default),
177 * then the raw binary data associated with elements will be kept in
178 * the raw_data field within the DGNElemCore when they are read. This
179 * is required if the application needs to interprete the raw data itself.
180 * It is also necessary if the element is to be written back to this file,
181 * or another file using DGNWriteElement(). Off by default (to conserve
182 * memory).
183 *
184 * @param hDGN handle to file returned by DGNOpen().
185 * @param nOptions ORed option flags.
186 */
187
DGNSetOptions(DGNHandle hDGN,int nOptions)188 void DGNSetOptions( DGNHandle hDGN, int nOptions )
189
190 {
191 DGNInfo *psDGN = (DGNInfo *) hDGN;
192
193 psDGN->options = nOptions;
194 }
195
196 /************************************************************************/
197 /* DGNSetSpatialFilter() */
198 /************************************************************************/
199
200 /**
201 * Set rectangle for which features are desired.
202 *
203 * If a spatial filter is set with this function, DGNReadElement() will
204 * only return spatial elements (elements with a known bounding box) and
205 * only those elements for which this bounding box overlaps the requested
206 * region.
207 *
208 * If all four values (dfXMin, dfXMax, dfYMin and dfYMax) are zero, the
209 * spatial filter is disabled. Note that installing a spatial filter
210 * won't reduce the amount of data read from disk. All elements are still
211 * scanned, but the amount of processing work for elements outside the
212 * spatial filter is minimized.
213 *
214 * @param hDGN Handle from DGNOpen() for file to update.
215 * @param dfXMin minimum x coordinate for extents (georeferenced coordinates).
216 * @param dfYMin minimum y coordinate for extents (georeferenced coordinates).
217 * @param dfXMax maximum x coordinate for extents (georeferenced coordinates).
218 * @param dfYMax maximum y coordinate for extents (georeferenced coordinates).
219 */
220
DGNSetSpatialFilter(DGNHandle hDGN,double dfXMin,double dfYMin,double dfXMax,double dfYMax)221 void DGNSetSpatialFilter( DGNHandle hDGN,
222 double dfXMin, double dfYMin,
223 double dfXMax, double dfYMax )
224
225 {
226 DGNInfo *psDGN = (DGNInfo *) hDGN;
227
228 if( dfXMin == 0.0 && dfXMax == 0.0
229 && dfYMin == 0.0 && dfYMax == 0.0 )
230 {
231 psDGN->has_spatial_filter = FALSE;
232 return;
233 }
234
235 psDGN->has_spatial_filter = TRUE;
236 psDGN->sf_converted_to_uor = FALSE;
237
238 psDGN->sf_min_x_geo = dfXMin;
239 psDGN->sf_min_y_geo = dfYMin;
240 psDGN->sf_max_x_geo = dfXMax;
241 psDGN->sf_max_y_geo = dfYMax;
242
243 DGNSpatialFilterToUOR( psDGN );
244
245 }
246
247 /************************************************************************/
248 /* DGNSpatialFilterToUOR() */
249 /************************************************************************/
250
DGNSpatialFilterToUOR(DGNInfo * psDGN)251 void DGNSpatialFilterToUOR( DGNInfo *psDGN )
252
253 {
254 DGNPoint sMin, sMax;
255
256 if( psDGN->sf_converted_to_uor
257 || !psDGN->has_spatial_filter
258 || !psDGN->got_tcb )
259 return;
260
261 sMin.x = psDGN->sf_min_x_geo;
262 sMin.y = psDGN->sf_min_y_geo;
263 sMin.z = 0;
264
265 sMax.x = psDGN->sf_max_x_geo;
266 sMax.y = psDGN->sf_max_y_geo;
267 sMax.z = 0;
268
269 DGNInverseTransformPoint( psDGN, &sMin );
270 DGNInverseTransformPoint( psDGN, &sMax );
271
272 psDGN->sf_min_x = (GUInt32) (sMin.x + 2147483648.0);
273 psDGN->sf_min_y = (GUInt32) (sMin.y + 2147483648.0);
274 psDGN->sf_max_x = (GUInt32) (sMax.x + 2147483648.0);
275 psDGN->sf_max_y = (GUInt32) (sMax.y + 2147483648.0);
276
277 psDGN->sf_converted_to_uor = TRUE;
278 }
279
280 /************************************************************************/
281 /* DGNClose() */
282 /************************************************************************/
283
284 /**
285 * Close DGN file.
286 *
287 * @param hDGN Handle from DGNOpen() for file to close.
288 */
289
DGNClose(DGNHandle hDGN)290 void DGNClose( DGNHandle hDGN )
291
292 {
293 DGNInfo *psDGN = (DGNInfo *) hDGN;
294
295 VSIFClose( psDGN->fp );
296 CPLFree( psDGN->element_index );
297 CPLFree( psDGN );
298 }
299
300 /************************************************************************/
301 /* DGNGetDimension() */
302 /************************************************************************/
303
304 /**
305 * Return 2D/3D dimension of file.
306 *
307 * Return 2 or 3 depending on the dimension value of the provided file.
308 */
309
DGNGetDimension(DGNHandle hDGN)310 int DGNGetDimension( DGNHandle hDGN )
311
312 {
313 DGNInfo *psDGN = (DGNInfo *) hDGN;
314
315 return psDGN->dimension;
316 }
317