1 /******************************************************************************
2 *
3 * Project: OpenGIS Simple Features Reference Implementation
4 * Purpose: Implements OGRPGDataSource class.
5 * Author: Frank Warmerdam, warmerdam@pobox.com
6 *
7 ******************************************************************************
8 * Copyright (c) 2000, Frank Warmerdam (warmerdam@pobox.com)
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 ****************************************************************************/
28
29 #include "ogr_dgn.h"
30 #include "cpl_conv.h"
31 #include "cpl_string.h"
32
33 CPL_CVSID("$Id: ogrdgndatasource.cpp 7e07230bbff24eb333608de4dbd460b7312839d0 2017-12-11 19:08:47Z Even Rouault $")
34
35 /************************************************************************/
36 /* OGRDGNDataSource() */
37 /************************************************************************/
38
OGRDGNDataSource()39 OGRDGNDataSource::OGRDGNDataSource() :
40 papoLayers(nullptr),
41 nLayers(0),
42 pszName(nullptr),
43 hDGN(nullptr),
44 papszOptions(nullptr)
45 {}
46
47 /************************************************************************/
48 /* ~OGRDGNDataSource() */
49 /************************************************************************/
50
~OGRDGNDataSource()51 OGRDGNDataSource::~OGRDGNDataSource()
52
53 {
54 for( int i = 0; i < nLayers; i++ )
55 delete papoLayers[i];
56
57 CPLFree( papoLayers );
58 CPLFree( pszName );
59 CSLDestroy( papszOptions );
60
61 if( hDGN != nullptr )
62 DGNClose( hDGN );
63 }
64
65 /************************************************************************/
66 /* Open() */
67 /************************************************************************/
68
Open(const char * pszNewName,int bTestOpen,int bUpdate)69 int OGRDGNDataSource::Open( const char * pszNewName,
70 int bTestOpen,
71 int bUpdate )
72
73 {
74 CPLAssert( nLayers == 0 );
75
76 /* -------------------------------------------------------------------- */
77 /* For now we require files to have the .dgn or .DGN */
78 /* extension. Eventually we will implement a more */
79 /* sophisticated test to see if it is a dgn file. */
80 /* -------------------------------------------------------------------- */
81 if( bTestOpen )
82 {
83
84 VSILFILE *fp = VSIFOpenL( pszNewName, "rb" );
85 if( fp == nullptr )
86 return FALSE;
87
88 GByte abyHeader[512];
89 const int nHeaderBytes = static_cast<int>(
90 VSIFReadL( abyHeader, 1, sizeof(abyHeader), fp ) );
91
92 VSIFCloseL( fp );
93
94 if( nHeaderBytes < 512 )
95 return FALSE;
96
97 if( !DGNTestOpen( abyHeader, nHeaderBytes ) )
98 return FALSE;
99 }
100
101 /* -------------------------------------------------------------------- */
102 /* Try to open the file as a DGN file. */
103 /* -------------------------------------------------------------------- */
104 hDGN = DGNOpen( pszNewName, bUpdate );
105 if( hDGN == nullptr )
106 {
107 if( !bTestOpen )
108 CPLError( CE_Failure, CPLE_AppDefined,
109 "Unable to open %s as a Microstation .dgn file.",
110 pszNewName );
111 return FALSE;
112 }
113
114 /* -------------------------------------------------------------------- */
115 /* Create the layer object. */
116 /* -------------------------------------------------------------------- */
117 OGRDGNLayer *poLayer = new OGRDGNLayer( "elements", hDGN, bUpdate );
118 pszName = CPLStrdup( pszNewName );
119
120 /* -------------------------------------------------------------------- */
121 /* Add layer to data source layer list. */
122 /* -------------------------------------------------------------------- */
123 papoLayers = static_cast<OGRDGNLayer **>(
124 CPLRealloc( papoLayers, sizeof(OGRDGNLayer *) * (nLayers+1) ) );
125 papoLayers[nLayers++] = poLayer;
126
127 return TRUE;
128 }
129
130 /************************************************************************/
131 /* TestCapability() */
132 /************************************************************************/
133
TestCapability(const char * pszCap)134 int OGRDGNDataSource::TestCapability( const char * pszCap )
135
136 {
137 if( EQUAL(pszCap,ODsCCreateLayer) )
138 return TRUE;
139
140 return FALSE;
141 }
142
143 /************************************************************************/
144 /* GetLayer() */
145 /************************************************************************/
146
GetLayer(int iLayer)147 OGRLayer *OGRDGNDataSource::GetLayer( int iLayer )
148
149 {
150 if( iLayer < 0 || iLayer >= nLayers )
151 return nullptr;
152
153 return papoLayers[iLayer];
154 }
155
156 /************************************************************************/
157 /* PreCreate() */
158 /* */
159 /* Called by OGRDGNDriver::Create() method to setup a stub */
160 /* OGRDataSource object without the associated file created */
161 /* yet. It will be created by theICreateLayer() call. */
162 /************************************************************************/
163
PreCreate(const char * pszFilename,char ** papszOptionsIn)164 bool OGRDGNDataSource::PreCreate( const char *pszFilename,
165 char **papszOptionsIn )
166
167 {
168 papszOptions = CSLDuplicate( papszOptionsIn );
169 pszName = CPLStrdup( pszFilename );
170
171 return true;
172 }
173
174 /************************************************************************/
175 /* ICreateLayer() */
176 /************************************************************************/
177
ICreateLayer(const char * pszLayerName,OGRSpatialReference * poSRS,OGRwkbGeometryType eGeomType,char ** papszExtraOptions)178 OGRLayer *OGRDGNDataSource::ICreateLayer( const char *pszLayerName,
179 OGRSpatialReference *poSRS,
180 OGRwkbGeometryType eGeomType,
181 char **papszExtraOptions )
182
183 {
184 /* -------------------------------------------------------------------- */
185 /* Ensure only one layer gets created. */
186 /* -------------------------------------------------------------------- */
187 if( nLayers > 0 )
188 {
189 CPLError( CE_Failure, CPLE_AppDefined,
190 "DGN driver only supports one layer with all the elements "
191 "in it." );
192 return nullptr;
193 }
194
195 /* -------------------------------------------------------------------- */
196 /* If the coordinate system is geographic, we should use a */
197 /* localized default origin and resolution. */
198 /* -------------------------------------------------------------------- */
199 const char *pszMasterUnit = "m";
200 const char *pszSubUnit = "cm";
201
202 int nUORPerSU = 1;
203 int nSUPerMU = 100;
204
205 double dfOriginX = -21474836.0; // Default origin centered on zero
206 double dfOriginY = -21474836.0; // with two decimals of precision.
207 double dfOriginZ = -21474836.0;
208
209 if( poSRS != nullptr && poSRS->IsGeographic() )
210 {
211 dfOriginX = -200.0;
212 dfOriginY = -200.0;
213
214 pszMasterUnit = "d";
215 pszSubUnit = "s";
216 nSUPerMU = 3600;
217 nUORPerSU = 1000;
218 }
219
220 /* -------------------------------------------------------------------- */
221 /* Parse out various creation options. */
222 /* -------------------------------------------------------------------- */
223 papszOptions = CSLInsertStrings( papszOptions, 0, papszExtraOptions );
224
225 const bool b3DRequested
226 = CPLFetchBool( papszOptions, "3D", wkbHasZ(eGeomType) );
227
228 const char *pszSeed = CSLFetchNameValue( papszOptions, "SEED" );
229 int nCreationFlags = 0;
230 if( pszSeed )
231 nCreationFlags |= DGNCF_USE_SEED_ORIGIN | DGNCF_USE_SEED_UNITS;
232 else if( b3DRequested )
233 pszSeed = CPLFindFile( "gdal", "seed_3d.dgn" );
234 else
235 pszSeed = CPLFindFile( "gdal", "seed_2d.dgn" );
236
237 if( pszSeed == nullptr )
238 {
239 CPLError( CE_Failure, CPLE_AppDefined,
240 "No seed file provided, and unable to find seed_2d.dgn." );
241 return nullptr;
242 }
243
244 if( CPLFetchBool( papszOptions, "COPY_WHOLE_SEED_FILE", true ) )
245 nCreationFlags |= DGNCF_COPY_WHOLE_SEED_FILE;
246 if( CPLFetchBool( papszOptions, "COPY_SEED_FILE_COLOR_TABLE", true ) )
247 nCreationFlags |= DGNCF_COPY_SEED_FILE_COLOR_TABLE;
248
249 const char *pszValue
250 = CSLFetchNameValue( papszOptions, "MASTER_UNIT_NAME" );
251 if( pszValue != nullptr )
252 {
253 nCreationFlags &= ~DGNCF_USE_SEED_UNITS;
254 pszMasterUnit = pszValue;
255 }
256
257 pszValue = CSLFetchNameValue( papszOptions, "SUB_UNIT_NAME" );
258 if( pszValue != nullptr )
259 {
260 nCreationFlags &= ~DGNCF_USE_SEED_UNITS;
261 pszSubUnit = pszValue;
262 }
263
264 pszValue = CSLFetchNameValue( papszOptions, "SUB_UNITS_PER_MASTER_UNIT" );
265 if( pszValue != nullptr )
266 {
267 nCreationFlags &= ~DGNCF_USE_SEED_UNITS;
268 nSUPerMU = atoi(pszValue);
269 }
270
271 pszValue = CSLFetchNameValue( papszOptions, "UOR_PER_SUB_UNIT" );
272 if( pszValue != nullptr )
273 {
274 nCreationFlags &= ~DGNCF_USE_SEED_UNITS;
275 nUORPerSU = atoi(pszValue);
276 }
277
278 pszValue = CSLFetchNameValue( papszOptions, "ORIGIN" );
279 if( pszValue != nullptr )
280 {
281 char **papszTuple = CSLTokenizeStringComplex( pszValue, " ,",
282 FALSE, FALSE );
283
284 nCreationFlags &= ~DGNCF_USE_SEED_ORIGIN;
285 if( CSLCount(papszTuple) == 3 )
286 {
287 dfOriginX = CPLAtof(papszTuple[0]);
288 dfOriginY = CPLAtof(papszTuple[1]);
289 dfOriginZ = CPLAtof(papszTuple[2]);
290 }
291 else if( CSLCount(papszTuple) == 2 )
292 {
293 dfOriginX = CPLAtof(papszTuple[0]);
294 dfOriginY = CPLAtof(papszTuple[1]);
295 dfOriginZ = 0.0;
296 }
297 else
298 {
299 CSLDestroy(papszTuple);
300 CPLError( CE_Failure, CPLE_AppDefined,
301 "ORIGIN is not a valid 2d or 3d tuple.\n"
302 "Separate tuple values with comma." );
303 return nullptr;
304 }
305 CSLDestroy(papszTuple);
306 }
307
308 /* -------------------------------------------------------------------- */
309 /* Try creating the base file. */
310 /* -------------------------------------------------------------------- */
311 hDGN = DGNCreate( pszName, pszSeed, nCreationFlags,
312 dfOriginX, dfOriginY, dfOriginZ,
313 nSUPerMU, nUORPerSU, pszMasterUnit, pszSubUnit );
314 if( hDGN == nullptr )
315 return nullptr;
316
317 /* -------------------------------------------------------------------- */
318 /* Create the layer object. */
319 /* -------------------------------------------------------------------- */
320 OGRDGNLayer *poLayer = new OGRDGNLayer( pszLayerName, hDGN, TRUE );
321
322 /* -------------------------------------------------------------------- */
323 /* Add layer to data source layer list. */
324 /* -------------------------------------------------------------------- */
325 papoLayers = static_cast<OGRDGNLayer **>(
326 CPLRealloc( papoLayers, sizeof(OGRDGNLayer *) * (nLayers+1) ) );
327 papoLayers[nLayers++] = poLayer;
328
329 return poLayer;
330 }
331