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