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