Line data Source code
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 : /************************************************************************/
34 : /* OGRDGNDataSource() */
35 : /************************************************************************/
36 :
37 74 : OGRDGNDataSource::OGRDGNDataSource()
38 : : papoLayers(nullptr), nLayers(0), pszName(nullptr), hDGN(nullptr),
39 74 : papszOptions(nullptr)
40 : {
41 74 : }
42 :
43 : /************************************************************************/
44 : /* ~OGRDGNDataSource() */
45 : /************************************************************************/
46 :
47 148 : OGRDGNDataSource::~OGRDGNDataSource()
48 :
49 : {
50 147 : for (int i = 0; i < nLayers; i++)
51 73 : delete papoLayers[i];
52 :
53 74 : CPLFree(papoLayers);
54 74 : CPLFree(pszName);
55 74 : CSLDestroy(papszOptions);
56 :
57 74 : if (hDGN != nullptr)
58 73 : DGNClose(hDGN);
59 148 : }
60 :
61 : /************************************************************************/
62 : /* Open() */
63 : /************************************************************************/
64 :
65 40 : int OGRDGNDataSource::Open(const char *pszNewName, int bTestOpen, int bUpdate)
66 :
67 : {
68 40 : CPLAssert(nLayers == 0);
69 :
70 : /* -------------------------------------------------------------------- */
71 : /* For now we require files to have the .dgn or .DGN */
72 : /* extension. Eventually we will implement a more */
73 : /* sophisticated test to see if it is a dgn file. */
74 : /* -------------------------------------------------------------------- */
75 40 : if (bTestOpen)
76 : {
77 :
78 40 : VSILFILE *fp = VSIFOpenL(pszNewName, "rb");
79 40 : if (fp == nullptr)
80 0 : return FALSE;
81 :
82 : GByte abyHeader[512];
83 : const int nHeaderBytes =
84 40 : static_cast<int>(VSIFReadL(abyHeader, 1, sizeof(abyHeader), fp));
85 :
86 40 : VSIFCloseL(fp);
87 :
88 40 : if (nHeaderBytes < 512)
89 0 : return FALSE;
90 :
91 40 : if (!DGNTestOpen(abyHeader, nHeaderBytes))
92 0 : return FALSE;
93 : }
94 :
95 : /* -------------------------------------------------------------------- */
96 : /* Try to open the file as a DGN file. */
97 : /* -------------------------------------------------------------------- */
98 40 : hDGN = DGNOpen(pszNewName, bUpdate);
99 40 : if (hDGN == nullptr)
100 : {
101 0 : if (!bTestOpen)
102 0 : CPLError(CE_Failure, CPLE_AppDefined,
103 : "Unable to open %s as a Microstation .dgn file.",
104 : pszNewName);
105 0 : return FALSE;
106 : }
107 :
108 : /* -------------------------------------------------------------------- */
109 : /* Create the layer object. */
110 : /* -------------------------------------------------------------------- */
111 40 : OGRDGNLayer *poLayer = new OGRDGNLayer(this, "elements", hDGN, bUpdate);
112 40 : pszName = CPLStrdup(pszNewName);
113 :
114 : /* -------------------------------------------------------------------- */
115 : /* Add layer to data source layer list. */
116 : /* -------------------------------------------------------------------- */
117 40 : papoLayers = static_cast<OGRDGNLayer **>(
118 40 : CPLRealloc(papoLayers, sizeof(OGRDGNLayer *) * (nLayers + 1)));
119 40 : papoLayers[nLayers++] = poLayer;
120 :
121 40 : return TRUE;
122 : }
123 :
124 : /************************************************************************/
125 : /* TestCapability() */
126 : /************************************************************************/
127 :
128 48 : int OGRDGNDataSource::TestCapability(const char *pszCap)
129 :
130 : {
131 48 : if (EQUAL(pszCap, ODsCCreateLayer))
132 32 : return TRUE;
133 16 : else if (EQUAL(pszCap, ODsCZGeometries))
134 0 : return TRUE;
135 :
136 16 : return FALSE;
137 : }
138 :
139 : /************************************************************************/
140 : /* GetLayer() */
141 : /************************************************************************/
142 :
143 11 : OGRLayer *OGRDGNDataSource::GetLayer(int iLayer)
144 :
145 : {
146 11 : if (iLayer < 0 || iLayer >= nLayers)
147 0 : return nullptr;
148 :
149 11 : return papoLayers[iLayer];
150 : }
151 :
152 : /************************************************************************/
153 : /* PreCreate() */
154 : /* */
155 : /* Called by OGRDGNDriver::Create() method to setup a stub */
156 : /* OGRDataSource object without the associated file created */
157 : /* yet. It will be created by theICreateLayer() call. */
158 : /************************************************************************/
159 :
160 34 : bool OGRDGNDataSource::PreCreate(const char *pszFilename, char **papszOptionsIn)
161 :
162 : {
163 34 : papszOptions = CSLDuplicate(papszOptionsIn);
164 34 : pszName = CPLStrdup(pszFilename);
165 :
166 34 : return true;
167 : }
168 :
169 : /************************************************************************/
170 : /* ICreateLayer() */
171 : /************************************************************************/
172 :
173 : OGRLayer *
174 50 : OGRDGNDataSource::ICreateLayer(const char *pszLayerName,
175 : const OGRGeomFieldDefn *poGeomFieldDefn,
176 : CSLConstList papszExtraOptions)
177 :
178 : {
179 : /* -------------------------------------------------------------------- */
180 : /* Ensure only one layer gets created. */
181 : /* -------------------------------------------------------------------- */
182 50 : if (nLayers > 0)
183 : {
184 16 : CPLError(CE_Failure, CPLE_AppDefined,
185 : "DGN driver only supports one layer with all the elements "
186 : "in it.");
187 16 : return nullptr;
188 : }
189 :
190 : const auto eGeomType =
191 34 : poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
192 : const auto poSRS =
193 34 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
194 :
195 : /* -------------------------------------------------------------------- */
196 : /* If the coordinate system is geographic, we should use a */
197 : /* localized default origin and resolution. */
198 : /* -------------------------------------------------------------------- */
199 34 : const char *pszMasterUnit = "m";
200 34 : const char *pszSubUnit = "cm";
201 :
202 34 : int nUORPerSU = 1;
203 34 : int nSUPerMU = 100;
204 :
205 34 : double dfOriginX = -21474836.0; // Default origin centered on zero
206 34 : double dfOriginY = -21474836.0; // with two decimals of precision.
207 34 : double dfOriginZ = -21474836.0;
208 :
209 34 : if (poSRS != nullptr && poSRS->IsGeographic())
210 : {
211 0 : dfOriginX = -200.0;
212 0 : dfOriginY = -200.0;
213 :
214 0 : pszMasterUnit = "d";
215 0 : pszSubUnit = "s";
216 0 : nSUPerMU = 3600;
217 0 : nUORPerSU = 1000;
218 : }
219 :
220 : /* -------------------------------------------------------------------- */
221 : /* Parse out various creation options. */
222 : /* -------------------------------------------------------------------- */
223 34 : papszOptions = CSLInsertStrings(papszOptions, 0, papszExtraOptions);
224 :
225 : const bool b3DRequested =
226 34 : CPLFetchBool(papszOptions, "3D", wkbHasZ(eGeomType));
227 :
228 34 : const char *pszSeed = CSLFetchNameValue(papszOptions, "SEED");
229 34 : int nCreationFlags = 0;
230 34 : if (pszSeed)
231 0 : nCreationFlags |= DGNCF_USE_SEED_ORIGIN | DGNCF_USE_SEED_UNITS;
232 34 : else if (b3DRequested)
233 14 : pszSeed = CPLFindFile("gdal", "seed_3d.dgn");
234 : else
235 20 : pszSeed = CPLFindFile("gdal", "seed_2d.dgn");
236 :
237 34 : if (pszSeed == nullptr)
238 : {
239 0 : CPLError(CE_Failure, CPLE_AppDefined,
240 : "No seed file provided, and unable to find seed_2d.dgn.");
241 0 : return nullptr;
242 : }
243 :
244 34 : if (CPLFetchBool(papszOptions, "COPY_WHOLE_SEED_FILE", true))
245 34 : nCreationFlags |= DGNCF_COPY_WHOLE_SEED_FILE;
246 34 : if (CPLFetchBool(papszOptions, "COPY_SEED_FILE_COLOR_TABLE", true))
247 34 : nCreationFlags |= DGNCF_COPY_SEED_FILE_COLOR_TABLE;
248 :
249 34 : const char *pszValue = CSLFetchNameValue(papszOptions, "MASTER_UNIT_NAME");
250 34 : if (pszValue != nullptr)
251 : {
252 0 : nCreationFlags &= ~DGNCF_USE_SEED_UNITS;
253 0 : pszMasterUnit = pszValue;
254 : }
255 :
256 34 : pszValue = CSLFetchNameValue(papszOptions, "SUB_UNIT_NAME");
257 34 : if (pszValue != nullptr)
258 : {
259 0 : nCreationFlags &= ~DGNCF_USE_SEED_UNITS;
260 0 : pszSubUnit = pszValue;
261 : }
262 :
263 34 : pszValue = CSLFetchNameValue(papszOptions, "SUB_UNITS_PER_MASTER_UNIT");
264 34 : if (pszValue != nullptr)
265 : {
266 1 : nCreationFlags &= ~DGNCF_USE_SEED_UNITS;
267 1 : nSUPerMU = atoi(pszValue);
268 : }
269 :
270 34 : pszValue = CSLFetchNameValue(papszOptions, "UOR_PER_SUB_UNIT");
271 34 : if (pszValue != nullptr)
272 : {
273 1 : nCreationFlags &= ~DGNCF_USE_SEED_UNITS;
274 1 : nUORPerSU = atoi(pszValue);
275 : }
276 :
277 34 : pszValue = CSLFetchNameValue(papszOptions, "ORIGIN");
278 34 : if (pszValue != nullptr)
279 : {
280 : char **papszTuple =
281 1 : CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE);
282 :
283 1 : nCreationFlags &= ~DGNCF_USE_SEED_ORIGIN;
284 1 : if (CSLCount(papszTuple) == 3)
285 : {
286 1 : dfOriginX = CPLAtof(papszTuple[0]);
287 1 : dfOriginY = CPLAtof(papszTuple[1]);
288 1 : dfOriginZ = CPLAtof(papszTuple[2]);
289 : }
290 0 : else if (CSLCount(papszTuple) == 2)
291 : {
292 0 : dfOriginX = CPLAtof(papszTuple[0]);
293 0 : dfOriginY = CPLAtof(papszTuple[1]);
294 0 : dfOriginZ = 0.0;
295 : }
296 : else
297 : {
298 0 : CSLDestroy(papszTuple);
299 0 : CPLError(CE_Failure, CPLE_AppDefined,
300 : "ORIGIN is not a valid 2d or 3d tuple.\n"
301 : "Separate tuple values with comma.");
302 0 : return nullptr;
303 : }
304 1 : CSLDestroy(papszTuple);
305 : }
306 :
307 : /* -------------------------------------------------------------------- */
308 : /* Try creating the base file. */
309 : /* -------------------------------------------------------------------- */
310 34 : hDGN = DGNCreate(pszName, pszSeed, nCreationFlags, dfOriginX, dfOriginY,
311 : dfOriginZ, nSUPerMU, nUORPerSU, pszMasterUnit, pszSubUnit);
312 34 : if (hDGN == nullptr)
313 1 : return nullptr;
314 :
315 : /* -------------------------------------------------------------------- */
316 : /* Create the layer object. */
317 : /* -------------------------------------------------------------------- */
318 33 : OGRDGNLayer *poLayer = new OGRDGNLayer(this, pszLayerName, hDGN, TRUE);
319 :
320 : /* -------------------------------------------------------------------- */
321 : /* Add layer to data source layer list. */
322 : /* -------------------------------------------------------------------- */
323 33 : papoLayers = static_cast<OGRDGNLayer **>(
324 33 : CPLRealloc(papoLayers, sizeof(OGRDGNLayer *) * (nLayers + 1)));
325 33 : papoLayers[nLayers++] = poLayer;
326 :
327 33 : return poLayer;
328 : }
|