1 /******************************************************************************
2 *
3 * Project: OpenGIS Simple Features Reference Implementation
4 * Purpose: OGR Driver for DGNv8
5 * Author: Even Rouault <even.rouault at spatialys.com>
6 *
7 ******************************************************************************
8 * Copyright (c) 2017, Even Rouault <even.rouault at spatialys.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_dgnv8.h"
30 #include "cpl_conv.h"
31 #include "cpl_string.h"
32
33 CPL_CVSID("$Id: ogrdgnv8datasource.cpp a3bd06c8ed331ed68d3e8d374ae512c26769a56a 2019-04-20 23:52:31 +0200 Even Rouault $")
34
35 /************************************************************************/
36 /* OGRDGNV8DataSource() */
37 /************************************************************************/
38
OGRDGNV8DataSource(OGRDGNV8Services * poServices)39 OGRDGNV8DataSource::OGRDGNV8DataSource(OGRDGNV8Services* poServices) :
40 m_poServices(poServices),
41 m_papoLayers(nullptr),
42 m_nLayers(0),
43 m_papszOptions(nullptr),
44 m_poDb(static_cast<const OdRxObject*>(nullptr)),
45 m_bUpdate(false),
46 m_bModified(false)
47 {}
48
49 /************************************************************************/
50 /* ~OGRDGNV8DataSource() */
51 /************************************************************************/
52
~OGRDGNV8DataSource()53 OGRDGNV8DataSource::~OGRDGNV8DataSource()
54
55 {
56 OGRDGNV8DataSource::FlushCache();
57
58 for( int i = 0; i < m_nLayers; i++ )
59 delete m_papoLayers[i];
60
61 CPLFree( m_papoLayers );
62 CSLDestroy( m_papszOptions );
63 }
64
65
66 /************************************************************************/
67 /* FlushCache() */
68 /************************************************************************/
69
FlushCache()70 void OGRDGNV8DataSource::FlushCache()
71 {
72 if( m_poDb.isNull() || !m_bModified )
73 return;
74 m_bModified = false;
75
76 for( int i = 0; i < m_nLayers; i++ )
77 {
78 m_papoLayers[i]->m_pModel->fitToView();
79 }
80
81 OdString oFilename( FromUTF8(GetDescription()) );
82 try
83 {
84 m_poDb->writeFile( oFilename );
85 }
86 catch (const OdError& e)
87 {
88 CPLError(CE_Failure, CPLE_AppDefined,
89 "Teigha DGN error occurred: %s",
90 ToUTF8(e.description()).c_str());
91 }
92 catch (const std::exception &exc)
93 {
94 CPLError(CE_Failure, CPLE_AppDefined,
95 "std::exception occurred: %s", exc.what());
96 }
97 catch (...)
98 {
99 CPLError(CE_Failure, CPLE_AppDefined,
100 "Unknown exception occurred");
101 }
102 }
103
104 /************************************************************************/
105 /* Open() */
106 /************************************************************************/
107
Open(const char * pszFilename,bool bUpdate)108 int OGRDGNV8DataSource::Open( const char * pszFilename, bool bUpdate )
109
110 {
111 SetDescription(pszFilename);
112
113 OdString oFilename( FromUTF8(pszFilename) );
114 try
115 {
116 m_poDb = m_poServices->readFile( oFilename );
117 }
118 catch (const OdError& e)
119 {
120 CPLError(CE_Failure, CPLE_AppDefined,
121 "Teigha DGN error occurred: %s",
122 ToUTF8(e.description()).c_str());
123 return FALSE;
124 }
125 catch (const std::exception &exc)
126 {
127 CPLError(CE_Failure, CPLE_AppDefined,
128 "std::exception occurred: %s", exc.what());
129 return FALSE;
130 }
131 catch (...)
132 {
133 CPLError(CE_Failure, CPLE_AppDefined,
134 "Unknown exception occurred");
135 return FALSE;
136 }
137
138 OdDgModelTablePtr pModelTable = m_poDb->getModelTable();
139 if (pModelTable.isNull())
140 {
141 CPLError(CE_Failure, CPLE_AppDefined,
142 "No model table found");
143 return FALSE;
144 }
145
146 // Loop over models
147 OdDgElementIteratorPtr pIter = pModelTable->createIterator();
148 for ( ; !pIter.isNull() && !pIter->done(); pIter->step() )
149 {
150 OdDgModelPtr pModel = OdDgModel::cast(
151 pIter->item().openObject(
152 bUpdate ? OdDg::kForWrite : OdDg::kForRead ) );
153 if ( !pModel.isNull() )
154 {
155 OGRDGNV8Layer* poLayer = new OGRDGNV8Layer(this, pModel);
156 m_papoLayers = static_cast<OGRDGNV8Layer**>(
157 CPLRealloc(m_papoLayers,
158 sizeof(OGRDGNV8Layer*) * (m_nLayers + 1)));
159 m_papoLayers[ m_nLayers++ ] = poLayer;
160 }
161 }
162
163 m_bUpdate = bUpdate;
164
165 return m_bUpdate || m_nLayers > 0;
166 }
167
168 /************************************************************************/
169 /* TestCapability() */
170 /************************************************************************/
171
TestCapability(const char * pszCap)172 int OGRDGNV8DataSource::TestCapability( const char * pszCap )
173
174 {
175 if( EQUAL(pszCap,ODsCCreateLayer) )
176 return m_bUpdate;
177 else if( EQUAL(pszCap,ODsCCurveGeometries) )
178 return TRUE;
179
180 return FALSE;
181 }
182
183 /************************************************************************/
184 /* GetLayer() */
185 /************************************************************************/
186
GetLayer(int iLayer)187 OGRLayer *OGRDGNV8DataSource::GetLayer( int iLayer )
188
189 {
190 if( iLayer < 0 || iLayer >= m_nLayers )
191 return nullptr;
192
193 return m_papoLayers[iLayer];
194 }
195
196 /************************************************************************/
197 /* EraseSubElements() */
198 /************************************************************************/
199
EraseSubElements(T container)200 template<class T> static void EraseSubElements(T container)
201 {
202 if( !container.isNull() )
203 {
204 OdDgElementIteratorPtr pIter = container->createIterator();
205 for(; !pIter.isNull() && !pIter->done(); pIter->step() )
206 {
207 OdDgElementPtr pElement =
208 pIter->item().openObject(OdDg::kForWrite);
209 if( !pElement.isNull() )
210 {
211 pElement->erase(true);
212 }
213 }
214 }
215 }
216
217 /************************************************************************/
218 /* InitWithSeed() */
219 /************************************************************************/
220
InitWithSeed()221 void OGRDGNV8DataSource::InitWithSeed()
222 {
223 #if 0
224 EraseSubElements(m_poDb->getLevelTable(OdDg::kForWrite));
225 #endif
226
227 if( !CPLTestBool(CSLFetchNameValueDef(
228 m_papszOptions, "COPY_SEED_FILE_COLOR_TABLE", "NO")) )
229 {
230 OdDgColorTablePtr colorTable = m_poDb->getColorTable(OdDg::kForWrite);
231 if( !colorTable.isNull() )
232 {
233 const ODCOLORREF* defColors = OdDgColorTable::defaultPalette();
234 OdArray<ODCOLORREF> palette;
235 palette.insert(palette.begin(), defColors, defColors + 256);
236 colorTable->setPalette(palette);
237 }
238 }
239
240 OdDgModelTablePtr pModelTable = m_poDb->getModelTable();
241
242 if( CPLTestBool(CSLFetchNameValueDef(
243 m_papszOptions, "COPY_SEED_FILE_MODEL", "YES")) )
244 {
245 if( !pModelTable.isNull() )
246 {
247 OdDgElementIteratorPtr pIter = pModelTable->createIterator();
248 for(; !pIter.isNull() && !pIter->done(); pIter->step() )
249 {
250 OdDgModelPtr pModel = OdDgModel::cast(
251 pIter->item().openObject( OdDg::kForWrite ) );
252 if( !pModel.isNull() )
253 {
254 OdDgElementIteratorPtr pIter2 =
255 pModel->createGraphicsElementsIterator();
256 for(; !pIter2.isNull() && !pIter2->done(); pIter2->step() )
257 {
258 OdDgElementPtr pElement =
259 pIter2->item().openObject(OdDg::kForWrite);
260 if( !pElement.isNull() )
261 {
262 pElement->erase(true);
263 }
264 }
265
266 if( !CPLTestBool(CSLFetchNameValueDef(
267 m_papszOptions,
268 "COPY_SEED_FILE_MODEL_CONTROL_ELEMENTS", "YES")) )
269 {
270 pIter2 =
271 pModel->createControlElementsIterator();
272 for(; !pIter2.isNull() && !pIter2->done();
273 pIter2->step() )
274 {
275 OdDgElementPtr pElement =
276 pIter2->item().openObject(OdDg::kForWrite);
277 if( !pElement.isNull() )
278 {
279 pElement->erase(true);
280 }
281 }
282 }
283 }
284 }
285 }
286 }
287 else
288 {
289 // Erase existing models
290 EraseSubElements(pModelTable);
291
292 // Recreate a new model and bind it as default
293 OdDgModelPtr model = OdDgModel::createObject();
294 pModelTable->add( model );
295
296 m_poDb->setActiveModelId( model->elementId() );
297 m_poDb->setDefaultModelId( model->elementId() );
298
299 // Erase existing views
300 EraseSubElements(m_poDb->getNamedViewTable());
301 OdDgViewGroupTablePtr pViewGroupTable = m_poDb->getViewGroupTable();
302 EraseSubElements(pViewGroupTable);
303
304 // Recreate a new view group and bind it as default
305 model->createViewGroup();
306
307 OdDgElementIteratorPtr pIter = pViewGroupTable->createIterator();
308 m_poDb->setActiveViewGroupId(pIter->item());
309 }
310
311 #if 0
312 CPLString osTmpFile(CPLString(GetDescription()) + ".tmp");
313 OdString odTmpFile( FromUTF8( osTmpFile ) );
314 m_poDb->writeFile( odTmpFile );
315 m_poDb = m_poServices->readFile( odTmpFile );
316 VSIUnlink(osTmpFile);
317 #endif
318 }
319
320 /************************************************************************/
321 /* FillMD() */
322 /************************************************************************/
323
FillMD(CPLStringList & osDGNMD,const char * pszKey,OdString str)324 static void FillMD( CPLStringList& osDGNMD, const char* pszKey, OdString str )
325 {
326 CPLString osVal( OGRDGNV8DataSource::ToUTF8(str) );
327 if( !osVal.empty() )
328 osDGNMD.SetNameValue(pszKey, osVal);
329 }
330
331 /************************************************************************/
332 /* GetMetadataDomainList() */
333 /************************************************************************/
334
GetMetadataDomainList()335 char **OGRDGNV8DataSource::GetMetadataDomainList()
336 {
337 return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(),
338 TRUE,
339 "DGN", nullptr);
340 }
341
342 /************************************************************************/
343 /* GetMetadata() */
344 /************************************************************************/
345
GetMetadata(const char * pszDomain)346 char** OGRDGNV8DataSource::GetMetadata(const char* pszDomain)
347 {
348 if( pszDomain != nullptr && EQUAL(pszDomain, "DGN") )
349 {
350 m_osDGNMD.Clear();
351 OdDgSummaryInformationPtr summary = oddgGetSummaryInformation(m_poDb);
352 FillMD( m_osDGNMD, "APPLICATION", summary->getApplicationName() );
353 FillMD( m_osDGNMD, "TITLE", summary->getTitle() );
354 FillMD( m_osDGNMD, "SUBJECT", summary->getSubject() );
355 FillMD( m_osDGNMD, "AUTHOR", summary->getAuthor() );
356 FillMD( m_osDGNMD, "KEYWORDS", summary->getKeywords() );
357 FillMD( m_osDGNMD, "TEMPLATE", summary->getTemplate() );
358 FillMD( m_osDGNMD, "COMMENTS", summary->getComments() );
359 FillMD( m_osDGNMD, "LAST_SAVED_BY", summary->getLastSavedBy() );
360 FillMD( m_osDGNMD, "REVISION_NUMBER", summary->getRevisionNumber() );
361 OdDgDocumentSummaryInformationPtr docSummaryInfo =
362 oddgGetDocumentSummaryInformation(m_poDb);
363 FillMD( m_osDGNMD, "CATEGORY", docSummaryInfo->getCategory() );
364 FillMD( m_osDGNMD, "MANAGER", docSummaryInfo->getManager() );
365 FillMD( m_osDGNMD, "COMPANY", docSummaryInfo->getCompany() );
366 return m_osDGNMD.List();
367 }
368 return GDALDataset::GetMetadata(pszDomain);
369 }
370
371 /************************************************************************/
372 /* GetMetadataItem() */
373 /************************************************************************/
374
GetMetadataItem(const char * pszName,const char * pszDomain)375 const char* OGRDGNV8DataSource::GetMetadataItem(const char* pszName,
376 const char* pszDomain)
377 {
378 return CSLFetchNameValue( GetMetadata(pszDomain), pszName );
379 }
380
381 /************************************************************************/
382 /* PreCreate() */
383 /* */
384 /* Called by OGRDGNV8DriverCreate() method to setup a stub */
385 /* OGRDataSource object without the associated file created */
386 /* yet. */
387 /************************************************************************/
388
PreCreate(const char * pszFilename,char ** papszOptionsIn)389 bool OGRDGNV8DataSource::PreCreate( const char *pszFilename,
390 char **papszOptionsIn )
391
392 {
393 m_bUpdate = true;
394 m_bModified = true;
395 m_papszOptions = CSLDuplicate( papszOptionsIn );
396 SetDescription( pszFilename );
397
398 VSILFILE* fp = VSIFOpenL(pszFilename, "wb");
399 if( fp == nullptr )
400 {
401 CPLError(CE_Failure, CPLE_FileIO,
402 "Cannot write %s", pszFilename);
403 return false;
404 }
405 VSIFCloseL(fp);
406
407 const char* pszSeed = CSLFetchNameValue(m_papszOptions, "SEED");
408
409 try
410 {
411 if( pszSeed )
412 m_poDb = m_poServices->readFile( FromUTF8(pszSeed) );
413 else
414 m_poDb = m_poServices->createDatabase();
415
416 if( pszSeed )
417 {
418 InitWithSeed();
419 }
420 }
421 catch (const OdError& e)
422 {
423 CPLError(CE_Failure, CPLE_AppDefined,
424 "Teigha DGN error occurred: %s",
425 ToUTF8(e.description()).c_str());
426 return false;
427 }
428 catch (const std::exception &exc)
429 {
430 CPLError(CE_Failure, CPLE_AppDefined,
431 "std::exception occurred: %s", exc.what());
432 return false;
433 }
434 catch (...)
435 {
436 CPLError(CE_Failure, CPLE_AppDefined,
437 "Unknown exception occurred");
438 return false;
439 }
440
441 OdDgSummaryInformationPtr summary = oddgGetSummaryInformation(m_poDb);
442 CPLString osDefaultAppName("GDAL ");
443 osDefaultAppName += GDALVersionInfo("RELEASE_NAME");
444 osDefaultAppName += " with " + ToUTF8(summary->getApplicationName());
445 const char* pszAppName = CSLFetchNameValue(m_papszOptions,
446 "APPLICATION");
447 if( pszSeed == nullptr && pszAppName == nullptr )
448 pszAppName = osDefaultAppName;
449 if( pszAppName )
450 summary->setApplicationName(FromUTF8(pszAppName));
451
452 const char* pszTitle = CSLFetchNameValue(m_papszOptions, "TITLE");
453 if( pszTitle )
454 summary->setTitle(FromUTF8(pszTitle));
455
456 const char* pszSubject = CSLFetchNameValue(m_papszOptions, "SUBJECT");
457 if( pszSubject )
458 summary->setSubject(FromUTF8(pszSubject));
459
460 const char* pszAuthor = CSLFetchNameValue(m_papszOptions, "AUTHOR");
461 if( pszAuthor )
462 summary->setAuthor(FromUTF8(pszAuthor));
463
464 const char* pszKeywords = CSLFetchNameValue(m_papszOptions, "KEYWORDS");
465 if( pszKeywords )
466 summary->setKeywords(FromUTF8(pszKeywords));
467
468 const char* pszTemplate = CSLFetchNameValue(m_papszOptions, "TEMPLATE");
469 if( pszTemplate )
470 summary->setTemplate(FromUTF8(pszTemplate));
471
472 const char* pszComments = CSLFetchNameValue(m_papszOptions, "COMMENTS");
473 if( pszComments )
474 summary->setComments(FromUTF8(pszComments));
475
476 const char* pszLastSavedBy = CSLFetchNameValue(m_papszOptions,
477 "LAST_SAVED_BY");
478 if( pszLastSavedBy )
479 summary->setLastSavedBy(FromUTF8(pszLastSavedBy));
480
481 const char* pszRevisionNumber = CSLFetchNameValue(m_papszOptions,
482 "REVISION_NUMBER");
483 if( pszRevisionNumber )
484 summary->setRevisionNumber(FromUTF8(pszRevisionNumber));
485
486 OdDgDocumentSummaryInformationPtr docSummaryInfo =
487 oddgGetDocumentSummaryInformation(m_poDb);
488
489 const char* pszCategory = CSLFetchNameValue(m_papszOptions, "CATEGORY");
490 if( pszCategory )
491 docSummaryInfo->setCategory(FromUTF8(pszCategory));
492
493 const char* pszManager = CSLFetchNameValue(m_papszOptions, "MANAGER");
494 if( pszManager )
495 docSummaryInfo->setManager(FromUTF8(pszManager));
496
497 const char* pszCompany = CSLFetchNameValue(m_papszOptions, "COMPANY");
498 if( pszCompany )
499 docSummaryInfo->setCompany(FromUTF8(pszCompany));
500
501 return true;
502 }
503
504 /************************************************************************/
505 /* ToUTF8() */
506 /************************************************************************/
507
ToUTF8(const OdString & str)508 CPLString OGRDGNV8DataSource::ToUTF8(const OdString& str)
509 {
510 CPL_STATIC_ASSERT( sizeof(OdChar) == sizeof(wchar_t) );
511 char* pszUTF8 = CPLRecodeFromWChar(
512 reinterpret_cast<const wchar_t*>(str.c_str()),
513 "WCHAR_T",
514 CPL_ENC_UTF8);
515 CPLString osRet(pszUTF8);
516 CPLFree(pszUTF8);
517 return osRet;
518 }
519
520 /************************************************************************/
521 /* FromUTF8() */
522 /************************************************************************/
523
FromUTF8(const CPLString & str)524 OdString OGRDGNV8DataSource::FromUTF8(const CPLString& str)
525 {
526 CPL_STATIC_ASSERT( sizeof(OdChar) == sizeof(wchar_t) );
527 OdChar* pwszWide = reinterpret_cast<OdChar*>(CPLRecodeToWChar(
528 str.c_str(),
529 CPL_ENC_UTF8,
530 "WCHAR_T"));
531 OdString osRet(pwszWide);
532 CPLFree(pwszWide);
533 return osRet;
534 }
535
536 /************************************************************************/
537 /* ICreateLayer() */
538 /************************************************************************/
539
ICreateLayer(const char * pszLayerName,OGRSpatialReference *,OGRwkbGeometryType,char ** papszOptions)540 OGRLayer *OGRDGNV8DataSource::ICreateLayer( const char *pszLayerName,
541 OGRSpatialReference * /*poSRS*/,
542 OGRwkbGeometryType /*eGeomType*/,
543 char **papszOptions )
544
545 {
546 if( !m_bUpdate )
547 {
548 CPLError( CE_Failure, CPLE_AppDefined,
549 "CreateLayer() only supported on update mode." );
550 return nullptr;
551 }
552
553 OdDgModelPtr model;
554 try
555 {
556 OdDgModelTablePtr pModelTable = m_poDb->getModelTable(OdDg::kForWrite);
557 // First try to find a model that matches the layer name (case of a seed
558 // file)
559 OdDgElementIteratorPtr pIter = pModelTable->createIterator();
560 for ( ; !pIter->done(); pIter->step() )
561 {
562 OdDgModelPtr pModel = OdDgModel::cast(
563 pIter->item().openObject( OdDg::kForWrite ) );
564 if ( !pModel.isNull() )
565 {
566 if( ToUTF8(pModel->getName()) == CPLString(pszLayerName) )
567 {
568 model = pModel;
569 break;
570 }
571 }
572 }
573 // If we don't find a match, but there's at least one model, pick
574 // the default one
575 if( model.isNull() && m_nLayers == 0 )
576 model = m_poDb->getActiveModelId().openObject( OdDg::kForWrite );
577 if( model.isNull() )
578 {
579 model = OdDgModel::createObject();
580 pModelTable->add( model );
581 }
582
583 const char* pszDim = CSLFetchNameValue(papszOptions, "DIM");
584 if( pszDim != nullptr )
585 {
586 model->setModelIs3dFlag( EQUAL(pszDim, "3") );
587 }
588
589 model->setWorkingUnit( OdDgModel::kWuMasterUnit );
590
591 model->setName( FromUTF8(pszLayerName) );
592
593 const char* pszDescription = CSLFetchNameValue(papszOptions,
594 "DESCRIPTION");
595 if( pszDescription )
596 model->setDescription( FromUTF8(pszDescription) );
597 }
598 catch (const OdError& e)
599 {
600 CPLError(CE_Failure, CPLE_AppDefined,
601 "Teigha DGN error occurred: %s",
602 ToUTF8(e.description()).c_str());
603 return nullptr;
604 }
605 catch (const std::exception &exc)
606 {
607 CPLError(CE_Failure, CPLE_AppDefined,
608 "std::exception occurred: %s", exc.what());
609 return nullptr;
610 }
611 catch (...)
612 {
613 CPLError(CE_Failure, CPLE_AppDefined,
614 "Unknown exception occurred");
615 return nullptr;
616 }
617
618 m_bModified = true;
619
620 OGRDGNV8Layer* poLayer = new OGRDGNV8Layer(this, model);
621 m_papoLayers = static_cast<OGRDGNV8Layer**>(
622 CPLRealloc(m_papoLayers,
623 sizeof(OGRDGNV8Layer*) * (m_nLayers + 1)));
624 m_papoLayers[ m_nLayers++ ] = poLayer;
625 return poLayer;
626 }
627