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