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