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 "ogr_featurestyle.h"
32 #include "ogr_api.h"
33 
34 #include <math.h>
35 #include <algorithm>
36 
37 /* -------------------------------------------------------------------- */
38 /*      Line Styles                                                     */
39 /* -------------------------------------------------------------------- */
40 #define DGNS_SOLID              0
41 #define DGNS_DOTTED             1
42 #define DGNS_MEDIUM_DASH        2
43 #define DGNS_LONG_DASH          3
44 #define DGNS_DOT_DASH           4
45 #define DGNS_SHORT_DASH         5
46 #define DGNS_DASH_DOUBLE_DOT    6
47 #define DGNS_LONG_DASH_SHORT_DASH 7
48 
49 constexpr double DEG_TO_RAD = M_PI / 180.0;
50 constexpr double RAD_TO_DEG = 180.0 / M_PI;
51 constexpr double CONTIGUITY_TOLERANCE = 1e10; // Arbitrary high value
52 
53 CPL_CVSID("$Id: ogrdgnv8layer.cpp fa752ad6eabafaf630a704e1892a9d837d683cb3 2021-03-06 17:04:38 +0100 Even Rouault $")
54 
ToUTF8(const OdString & str)55 static CPLString ToUTF8(const OdString& str)
56 {
57     return OGRDGNV8DataSource::ToUTF8(str);
58 }
59 
60 /************************************************************************/
61 /*                       EscapeDoubleQuote()                            */
62 /************************************************************************/
63 
EscapeDoubleQuote(const char * pszStr)64 static CPLString EscapeDoubleQuote(const char* pszStr)
65 {
66     if( strchr( pszStr, '"') != nullptr )
67     {
68         CPLString osEscaped;
69 
70         for( size_t iC = 0; pszStr[iC] != '\0'; iC++ )
71         {
72             if( pszStr[iC] == '"' )
73                 osEscaped += "\\\"";
74             else
75                 osEscaped += pszStr[iC];
76         }
77         return osEscaped;
78     }
79     else
80     {
81         return pszStr;
82     }
83 }
84 
85 /************************************************************************/
86 /*                          OGRDGNV8Layer()                             */
87 /************************************************************************/
88 
OGRDGNV8Layer(OGRDGNV8DataSource * poDS,OdDgModelPtr pModel)89 OGRDGNV8Layer::OGRDGNV8Layer( OGRDGNV8DataSource* poDS,
90                               OdDgModelPtr pModel ) :
91     m_poDS(poDS),
92     m_poFeatureDefn(nullptr),
93     m_pModel(pModel),
94     m_pIterator(static_cast<OdDgElementIterator*>(nullptr)),
95     m_nIdxInPendingFeatures(0)
96 {
97     const char* pszName;
98     if( pModel->getName().isEmpty() )
99         pszName = CPLSPrintf("Model #%d", pModel->getEntryId());
100     else
101         pszName = CPLSPrintf("%s", ToUTF8(pModel->getName()).c_str());
102     CPLDebug("DGNV8", "%s is %dd", pszName,
103              pModel->getModelIs3dFlag() ? 3 : 2);
104 
105 /* -------------------------------------------------------------------- */
106 /*      Create the feature definition.                                  */
107 /* -------------------------------------------------------------------- */
108     m_poFeatureDefn = new OGRFeatureDefn( pszName );
109     SetDescription( m_poFeatureDefn->GetName() );
110     m_poFeatureDefn->Reference();
111 
112     OGRFieldDefn oField( "", OFTInteger );
113 
114 /* -------------------------------------------------------------------- */
115 /*      Element type                                                    */
116 /* -------------------------------------------------------------------- */
117     oField.SetName( "Type" );
118     oField.SetType( OFTInteger );
119     oField.SetWidth( 2 );
120     oField.SetPrecision( 0 );
121     m_poFeatureDefn->AddFieldDefn( &oField );
122 
123 /* -------------------------------------------------------------------- */
124 /*      Level number.                                                   */
125 /* -------------------------------------------------------------------- */
126     oField.SetName( "Level" );
127     oField.SetType( OFTInteger );
128     oField.SetWidth( 2 );
129     oField.SetPrecision( 0 );
130     m_poFeatureDefn->AddFieldDefn( &oField );
131 
132 /* -------------------------------------------------------------------- */
133 /*      graphic group                                                   */
134 /* -------------------------------------------------------------------- */
135     oField.SetName( "GraphicGroup" );
136     oField.SetType( OFTInteger );
137     oField.SetWidth( 4 );
138     oField.SetPrecision( 0 );
139     m_poFeatureDefn->AddFieldDefn( &oField );
140 
141 /* -------------------------------------------------------------------- */
142 /*      ColorIndex                                                      */
143 /* -------------------------------------------------------------------- */
144     oField.SetName( "ColorIndex" );
145     oField.SetType( OFTInteger );
146     oField.SetWidth( 3 );
147     oField.SetPrecision( 0 );
148     m_poFeatureDefn->AddFieldDefn( &oField );
149 
150 /* -------------------------------------------------------------------- */
151 /*      Weight                                                          */
152 /* -------------------------------------------------------------------- */
153     oField.SetName( "Weight" );
154     oField.SetType( OFTInteger );
155     oField.SetWidth( 2 );
156     oField.SetPrecision( 0 );
157     m_poFeatureDefn->AddFieldDefn( &oField );
158 
159 /* -------------------------------------------------------------------- */
160 /*      Style                                                           */
161 /* -------------------------------------------------------------------- */
162     oField.SetName( "Style" );
163     oField.SetType( OFTInteger );
164     oField.SetWidth( 1 );
165     oField.SetPrecision( 0 );
166     m_poFeatureDefn->AddFieldDefn( &oField );
167 
168 /* -------------------------------------------------------------------- */
169 /*      Text                                                            */
170 /* -------------------------------------------------------------------- */
171     oField.SetName( "Text" );
172     oField.SetType( OFTString );
173     oField.SetWidth( 0 );
174     oField.SetPrecision( 0 );
175     m_poFeatureDefn->AddFieldDefn( &oField );
176 
177 /* -------------------------------------------------------------------- */
178 /*      ULink                                                           */
179 /* -------------------------------------------------------------------- */
180     oField.SetName( "ULink" );
181     oField.SetType( OFTString );
182     oField.SetSubType( OFSTJSON );
183     oField.SetWidth( 0 );
184     oField.SetPrecision( 0 );
185     m_poFeatureDefn->AddFieldDefn( &oField );
186 
187     OGRDGNV8Layer::ResetReading();
188 }
189 
190 /************************************************************************/
191 /*                          ~OGRDGNV8Layer()                            */
192 /************************************************************************/
193 
~OGRDGNV8Layer()194 OGRDGNV8Layer::~OGRDGNV8Layer()
195 
196 {
197     CleanPendingFeatures();
198     m_poFeatureDefn->Release();
199 }
200 /************************************************************************/
201 /*                       CleanPendingFeatures()                         */
202 /************************************************************************/
203 
CleanPendingFeatures()204 void OGRDGNV8Layer::CleanPendingFeatures()
205 
206 {
207     for( size_t i = m_nIdxInPendingFeatures;
208                 i < m_aoPendingFeatures.size(); i++ )
209     {
210         delete m_aoPendingFeatures[i].first;
211     }
212     m_aoPendingFeatures.clear();
213     m_nIdxInPendingFeatures = 0;
214 }
215 
216 /************************************************************************/
217 /*                            ResetReading()                            */
218 /************************************************************************/
219 
ResetReading()220 void OGRDGNV8Layer::ResetReading()
221 
222 {
223     if( m_pModel.get() )
224     {
225         m_pIterator = m_pModel->createGraphicsElementsIterator();
226     }
227     CleanPendingFeatures();
228 }
229 
230 /************************************************************************/
231 /*                         CollectSubElements()                         */
232 /************************************************************************/
233 
CollectSubElements(OdDgElementIteratorPtr iterator,int level)234 std::vector<tPairFeatureHoleFlag> OGRDGNV8Layer::CollectSubElements(
235                             OdDgElementIteratorPtr iterator, int level )
236 {
237     std::vector<tPairFeatureHoleFlag> oVector;
238 #ifdef DEBUG_VERBOSE
239     CPLString osIndent;
240     for(int i=0;i<level;i++)
241         osIndent += "  ";
242     int counter = 0;
243 #endif
244     for( ; !iterator->done(); iterator->step())
245     {
246 #ifdef DEBUG_VERBOSE
247         fprintf(stdout, "%sSub element %d:\n", osIndent.c_str(), counter );
248         counter ++;
249 #endif
250         OdRxObjectPtr object = iterator->item().openObject( OdDg::kForRead );
251         OdDgGraphicsElementPtr element = OdDgGraphicsElement::cast( object );
252         if (element.isNull())
253             continue;
254 
255         std::vector<tPairFeatureHoleFlag>  oSubVector =
256             ProcessElement(element, level + 1);
257         oVector.insert(oVector.end(), oSubVector.begin(), oSubVector.end());
258     }
259     return oVector;
260 }
261 
262 /************************************************************************/
263 /*                        GetAnchorPosition()                           */
264 /************************************************************************/
265 
GetAnchorPosition(OdDg::TextJustification value)266 static int GetAnchorPosition( OdDg::TextJustification value )
267 {
268     switch( value )
269     {
270         case OdDg::kLeftTop           : return 7;
271         case OdDg::kLeftCenter        : return 4;
272         case OdDg::kLeftBottom        : return 10;
273         case OdDg::kLeftMarginTop     : return 7;
274         case OdDg::kLeftMarginCenter  : return 4;
275         case OdDg::kLeftMarginBottom  : return 10;
276         case OdDg::kCenterTop         : return 8;
277         case OdDg::kCenterCenter      : return 5;
278         case OdDg::kCenterBottom      : return 11;
279         case OdDg::kRightMarginTop    : return 9;
280         case OdDg::kRightMarginCenter : return 6;
281         case OdDg::kRightMarginBottom : return 12;
282         case OdDg::kRightTop          : return 9;
283         case OdDg::kRightCenter       : return 6;
284         case OdDg::kRightBottom       : return 12;
285         case OdDg::kLeftDescender     : return 1;
286         case OdDg::kCenterDescender   : return 2;
287         case OdDg::kRightDescender    : return 3;
288         default                       : return 0;
289     }
290 }
291 
292 /************************************************************************/
293 /*                        GetAnchorPosition()                           */
294 /************************************************************************/
295 
GetAnchorPositionFromOGR(int value)296 static OdDg::TextJustification GetAnchorPositionFromOGR( int value )
297 {
298     switch( value )
299     {
300         case 1:  return OdDg::kLeftDescender;
301         case 2:  return OdDg::kCenterDescender;
302         case 3:  return OdDg::kRightDescender;
303         case 4:  return OdDg::kLeftCenter;
304         case 5:  return OdDg::kCenterCenter;
305         case 6:  return OdDg::kRightCenter;
306         case 7:  return OdDg::kLeftTop;
307         case 8:  return OdDg::kCenterTop;
308         case 9:  return OdDg::kRightTop;
309         case 10: return OdDg::kLeftBottom;
310         case 11: return OdDg::kCenterBottom;
311         case 12: return OdDg::kRightBottom;
312         default: return OdDg::kLeftTop;
313     }
314 }
315 
316 /************************************************************************/
317 /*                           AlmostEqual()                              */
318 /************************************************************************/
319 
AlmostEqual(double dfA,double dfB)320 static bool AlmostEqual(double dfA, double dfB)
321 {
322     if( fabs(dfB) > 1e-7 )
323         return fabs((dfA - dfB) / dfB) < 1e-6;
324     else
325         return fabs(dfA) <= 1e-7;
326 }
327 
328 /************************************************************************/
329 /*                         ProcessTextTraits                           */
330 /************************************************************************/
331 
332 template<class TextPtr> struct ProcessTextTraits
333 {
334 };
335 
336 template<> struct ProcessTextTraits<OdDgText2dPtr>
337 {
getRotationProcessTextTraits338     static double getRotation(OdDgText2dPtr text)
339     {
340         return text->getRotation();
341     }
342 
setGeomProcessTextTraits343     static void setGeom(OGRFeature* poFeature, OdDgText2dPtr text)
344     {
345         OdGePoint2d point = text->getOrigin();
346         poFeature->SetGeometryDirectly(
347                 new OGRPoint(point.x, point.y) );
348     }
349 };
350 
351 template<> struct ProcessTextTraits<OdDgText3dPtr>
352 {
getRotationProcessTextTraits353     static double getRotation(OdDgText3dPtr /*text*/)
354     {
355         return 0.0;
356     }
357 
setGeomProcessTextTraits358     static void setGeom(OGRFeature* poFeature, OdDgText3dPtr text)
359     {
360         OdGePoint3d point = text->getOrigin();
361         poFeature->SetGeometryDirectly(
362                 new OGRPoint(point.x, point.y, point.z) );
363     }
364 };
365 
366 /************************************************************************/
367 /*                           ProcessText()                              */
368 /************************************************************************/
369 
370 template<class TextPtr >
ProcessText(OGRFeature * poFeature,const CPLString & osColor,TextPtr text)371 static void ProcessText(OGRFeature* poFeature,
372                         const CPLString& osColor,
373                         TextPtr text)
374 {
375         const OdString oText = text->getText();
376         poFeature->SetField( "Text", ToUTF8(oText).c_str());
377 
378         CPLString osStyle;
379         osStyle.Printf("LABEL(t:\"%s\"",
380                         EscapeDoubleQuote(ToUTF8(oText)).c_str());
381         osStyle += osColor;
382         osStyle += CPLSPrintf(",s:%fg", text->getHeightMultiplier());
383 
384         // Gets Font name
385         OdDgFontTablePtr pFontTable = text->database()->getFontTable();
386         OdDgFontTableRecordPtr pFont =
387             pFontTable->getFont(text->getFontEntryId());
388         if (!pFont.isNull())
389         {
390             osStyle += CPLSPrintf(",f:\"%s\"",
391                 EscapeDoubleQuote(ToUTF8(pFont->getName())).c_str() );
392         }
393         else
394         {
395             osStyle += CPLSPrintf(",f:MstnFont%u",
396                 text->getFontEntryId() );
397         }
398 
399         const int nAnchor = GetAnchorPosition(text->getJustification());
400         if( nAnchor > 0 )
401             osStyle += CPLSPrintf(",p:%d", nAnchor);
402 
403         // Add the angle, if not horizontal
404         const double dfRotation =
405                         ProcessTextTraits<TextPtr>::getRotation(text);
406         if( dfRotation != 0.0 )
407           osStyle += CPLSPrintf(",a:%d",
408                    static_cast<int>(floor(dfRotation * RAD_TO_DEG +0.5)) );
409 
410         osStyle += ")";
411         poFeature->SetStyleString(osStyle);
412         ProcessTextTraits<TextPtr>::setGeom( poFeature, text );
413 }
414 
415 /************************************************************************/
416 /*                            ConsiderBrush()                           */
417 /************************************************************************/
418 
ConsiderBrush(OdDgGraphicsElementPtr element,const CPLString & osStyle)419 static CPLString ConsiderBrush(OdDgGraphicsElementPtr element,
420                                        const CPLString& osStyle)
421 {
422     CPLString osNewStyle(osStyle);
423     OdRxObjectPtrArray linkages;
424     element->getLinkages(OdDgAttributeLinkage::kFillStyle, linkages);
425     if( linkages.length() >= 1 )
426     {
427         OdDgFillColorLinkagePtr fillColor =
428             OdDgFillColorLinkage::cast( linkages[0] );
429         if( !fillColor.isNull() )
430         {
431             const OdUInt32 uFillColorIdx = fillColor->getColorIndex();
432             if( OdDgColorTable::isCorrectIndex(
433                                     element->database(), uFillColorIdx  ) )
434             {
435                 ODCOLORREF color = OdDgColorTable::lookupRGB(
436                     element->database(), uFillColorIdx  );
437                 const char* pszBrush = CPLSPrintf(
438                     "BRUSH(fc:#%02x%02x%02x,id:\"ogr-brush-0\")",
439                     ODGETRED(color),
440                     ODGETGREEN(color),
441                     ODGETBLUE(color));
442                 const OdUInt32 uColorIndex = element->getColorIndex();
443                 if( uFillColorIdx != uColorIndex )
444                 {
445                     osNewStyle = CPLString(pszBrush) + ";" + osNewStyle;
446                 }
447                 else
448                 {
449                     osNewStyle = pszBrush;
450                 }
451             }
452         }
453     }
454     return osNewStyle;
455 }
456 
457 /************************************************************************/
458 /*                         ProcessCurveTraits                           */
459 /************************************************************************/
460 
461 template<class CurveElementPtr> struct ProcessCurveTraits
462 {
463 };
464 
465 template<> struct ProcessCurveTraits<OdDgCurveElement2dPtr>
466 {
467     typedef OdDgCurve2d         CurveType;
468     typedef OdDgArc2d           ArcType;
469     typedef OdDgBSplineCurve2d  BSplineType;
470     typedef OdDgEllipse2d       EllipseType;
471     typedef OdGePoint2d         PointType;
472 
setPointProcessCurveTraits473     static void setPoint( OGRSimpleCurve* poSC, int i, OdGePoint2d point)
474     {
475         poSC->setPoint(i, point.x, point.y);
476     }
477 };
478 
479 template<> struct ProcessCurveTraits<OdDgCurveElement3dPtr>
480 {
481     typedef OdDgCurve3d         CurveType;
482     typedef OdDgArc3d           ArcType;
483     typedef OdDgBSplineCurve3d  BSplineType;
484     typedef OdDgEllipse3d       EllipseType;
485     typedef OdGePoint3d         PointType;
486 
setPointProcessCurveTraits487     static void setPoint( OGRSimpleCurve* poSC, int i, OdGePoint3d point)
488     {
489         poSC->setPoint(i, point.x, point.y, point.z);
490     }
491 };
492 
493 /************************************************************************/
494 /*                            ProcessCurve()                            */
495 /************************************************************************/
496 
497 template<class CurveElementPtr>
ProcessCurve(OGRFeature * poFeature,const CPLString & osPen,CurveElementPtr curveElement)498 static void ProcessCurve(OGRFeature* poFeature, const CPLString& osPen,
499                          CurveElementPtr curveElement)
500 {
501     typedef typename ProcessCurveTraits<CurveElementPtr>::CurveType CurveType;
502     typedef typename ProcessCurveTraits<CurveElementPtr>::ArcType ArcType;
503     typedef typename ProcessCurveTraits<CurveElementPtr>::BSplineType
504                                                                  BSplineType;
505     typedef typename ProcessCurveTraits<CurveElementPtr>::EllipseType
506                                                                  EllipseType;
507     typedef typename ProcessCurveTraits<CurveElementPtr>::PointType PointType;
508 
509     OdSmartPtr<CurveType> curve = CurveType::cast( curveElement );
510     OdSmartPtr<ArcType> arc = ArcType::cast( curveElement );
511     OdSmartPtr<BSplineType> bspline = BSplineType::cast( curveElement );
512     OdSmartPtr<EllipseType> ellipse = EllipseType::cast( curveElement );
513 
514     bool bIsCircular = false;
515     if( !ellipse.isNull() )
516     {
517         bIsCircular = AlmostEqual(ellipse->getPrimaryAxis(),
518                                   ellipse->getSecondaryAxis());
519     }
520     else if( !arc.isNull() )
521     {
522         bIsCircular = AlmostEqual(arc->getPrimaryAxis(),
523                                   arc->getSecondaryAxis());
524     }
525 
526     double dfStartParam, dfEndParam;
527     OdResult eRes = curveElement->getStartParam(dfStartParam);
528     CPL_IGNORE_RET_VAL(eRes);
529     CPLAssert(eRes == eOk );
530     eRes = curveElement->getEndParam(dfEndParam);
531     CPL_IGNORE_RET_VAL(eRes);
532     CPLAssert(eRes == eOk );
533 
534     CPLString osStyle(osPen);
535     bool bIsFilled = false;
536     if( !ellipse.isNull() )
537     {
538         osStyle = ConsiderBrush(curveElement, osPen);
539         bIsFilled = osStyle.find("BRUSH") == 0;
540     }
541 
542     OGRSimpleCurve* poSC;
543     int nPoints;
544     if( !bspline.isNull() )
545     {
546         // 10 is somewhat arbitrary
547         nPoints = 10 * bspline->numControlPoints();
548         poSC = new OGRLineString();
549     }
550     else if( !curve.isNull() )
551     {
552         // 5 is what is used in DGN driver
553         nPoints = 5 * curve->getVerticesCount();
554         poSC = new OGRLineString();
555     }
556     else if( bIsCircular )
557     {
558         poSC = new OGRCircularString();
559         if( !ellipse.isNull() )
560         {
561             nPoints = 5;
562         }
563         else
564         {
565             nPoints = 3;
566         }
567     }
568     else
569     {
570         if( bIsFilled )
571             poSC = new OGRLinearRing();
572         else
573             poSC = new OGRLineString();
574         const double dfArcStepSize =
575             CPLAtofM(CPLGetConfigOption("OGR_ARC_STEPSIZE", "4"));
576         if( !ellipse.isNull() )
577         {
578             nPoints = std::max(2,
579                 static_cast<int>(360 / dfArcStepSize));
580         }
581         else
582         {
583             nPoints = std::max(2,
584                 static_cast<int>(arc->getSweepAngle() *
585                                  RAD_TO_DEG / dfArcStepSize) );
586         }
587     }
588 
589     poSC->setNumPoints(nPoints);
590     for( int i = 0; i < nPoints; i++ )
591     {
592         PointType point;
593         double dfParam = dfStartParam + i *
594                 (dfEndParam - dfStartParam) / (nPoints - 1);
595         eRes = curveElement->getPointAtParam(dfParam, point);
596         CPL_IGNORE_RET_VAL(eRes);
597         CPLAssert(eRes == eOk );
598         ProcessCurveTraits<CurveElementPtr>::setPoint(poSC, i, point);
599     }
600 
601     if( bIsFilled )
602     {
603         if( bIsCircular )
604         {
605             OGRCurvePolygon* poCP = new OGRCurvePolygon();
606             poCP->addRingDirectly(poSC);
607             poFeature->SetGeometryDirectly( poCP );
608         }
609         else
610         {
611             OGRPolygon* poPoly = new OGRPolygon();
612             poPoly->addRingDirectly(poSC);
613             poFeature->SetGeometryDirectly( poPoly );
614         }
615     }
616     else
617     {
618         poFeature->SetGeometryDirectly( poSC );
619     }
620     poFeature->SetStyleString(osStyle);
621 }
622 
623 /************************************************************************/
624 /*                           IsContiguous()                             */
625 /************************************************************************/
626 
627 static
IsContiguous(const std::vector<tPairFeatureHoleFlag> & oVectorSubElts,bool & bHasCurves,bool & bIsClosed)628 bool IsContiguous( const std::vector<tPairFeatureHoleFlag>& oVectorSubElts,
629                    bool& bHasCurves,
630                    bool& bIsClosed )
631 {
632     bHasCurves = false;
633     bIsClosed = false;
634     OGRPoint oFirstPoint, oLastPoint;
635     bool bLastPointValid = false;
636     bool bIsContiguous = true;
637     for( size_t i = 0; i < oVectorSubElts.size(); i++ )
638     {
639         OGRGeometry* poGeom = oVectorSubElts[i].first->GetGeometryRef();
640         if( poGeom != nullptr )
641         {
642             OGRwkbGeometryType eType =
643                                 wkbFlatten(poGeom->getGeometryType());
644             if( eType == wkbCircularString )
645             {
646                 bHasCurves = true;
647             }
648             if( bIsContiguous &&
649                 (eType == wkbCircularString ||
650                  eType == wkbLineString) )
651             {
652                 OGRCurve* poCurve = poGeom->toCurve();
653                 if( poCurve->getNumPoints() >= 2 )
654                 {
655                     OGRPoint oStartPoint;
656                     poCurve->StartPoint( &oStartPoint );
657 
658                     if( bLastPointValid )
659                     {
660                         if( !AlmostEqual(oStartPoint.getX(),
661                                          oLastPoint.getX()) ||
662                             !AlmostEqual(oStartPoint.getY(),
663                                          oLastPoint.getY()) ||
664                             !AlmostEqual(oStartPoint.getZ(),
665                                          oLastPoint.getZ()) )
666                         {
667                              bIsContiguous = false;
668                              break;
669                         }
670                     }
671                     else
672                     {
673                         oFirstPoint = oStartPoint;
674                     }
675                     bLastPointValid = true;
676                     poCurve->EndPoint( &oLastPoint );
677                 }
678                 else
679                 {
680                     bIsContiguous = false;
681                     break;
682                 }
683             }
684             else
685             {
686                 bIsContiguous = false;
687                 break;
688             }
689         }
690     }
691     if( bIsContiguous )
692     {
693         bIsClosed = bLastPointValid &&
694                 AlmostEqual(oFirstPoint.getX(), oLastPoint.getX()) &&
695                 AlmostEqual(oFirstPoint.getY(), oLastPoint.getY()) &&
696                 AlmostEqual(oFirstPoint.getZ(), oLastPoint.getZ());
697     }
698     return bIsContiguous;
699 }
700 
701 /************************************************************************/
702 /*                           ProcessElement()                           */
703 /************************************************************************/
704 
ProcessElement(OdDgGraphicsElementPtr element,int level)705 std::vector<tPairFeatureHoleFlag> OGRDGNV8Layer::ProcessElement(
706                                             OdDgGraphicsElementPtr element,
707                                             int level)
708 {
709     std::vector<tPairFeatureHoleFlag> oVector;
710 #ifdef DEBUG_VERBOSE
711     CPLString osIndent;
712     for(int i=0;i<level;i++)
713         osIndent += "  ";
714 #endif
715 
716     bool bHoleFlag = false;
717     OGRFeature  *poFeature = new OGRFeature( m_poFeatureDefn );
718 
719     OdRxClass *poClass = element->isA();
720     const OdString osName = poClass->name();
721     const char *pszEntityClassName = static_cast<const char*>(osName);
722     poFeature->SetFID(
723         static_cast<GIntBig>(
724             static_cast<OdUInt64>(element->elementId().getHandle()) ) );
725 #ifdef DEBUG_VERBOSE
726     fprintf(stdout, "%s%s\n", osIndent.c_str(), pszEntityClassName);
727     fprintf(stdout, "%sID = %s\n",
728             osIndent.c_str(),
729             static_cast<const char*>(element->elementId().getHandle().ascii()) );
730     fprintf(stdout, "%sType = %d\n",
731             osIndent.c_str(), element->getElementType() );
732 #endif
733 
734     poFeature->SetField("Type", element->getElementType() );
735     const int nLevel = static_cast<int>(element->getLevelEntryId());
736     poFeature->SetField("Level", nLevel );
737     poFeature->SetField("GraphicGroup",
738                         static_cast<int>(element->getGraphicsGroupEntryId()) );
739     const OdUInt32 uColorIndex = element->getColorIndex();
740     CPLString osColor;
741     if( uColorIndex != OdDg::kColorByLevel &&
742         uColorIndex != OdDg::kColorByCell )
743     {
744         poFeature->SetField("ColorIndex",
745                             static_cast<int>(uColorIndex));
746 
747         const ODCOLORREF color = element->getColor();
748         osColor = CPLSPrintf(",c:#%02x%02x%02x",
749                               ODGETRED(color),
750                               ODGETGREEN(color),
751                               ODGETBLUE(color));
752     }
753     const OdInt32 nLineStyle = element->getLineStyleEntryId();
754     if( nLineStyle != OdDg::kLineStyleByLevel &&
755         nLineStyle != OdDg::kLineStyleByCell )
756     {
757         poFeature->SetField("Style", nLineStyle);
758     }
759 
760     const OdUInt32 uLineWeight = element->getLineWeight();
761     int nLineWeight = 0;
762     if( uLineWeight != OdDg::kLineWeightByLevel &&
763         uLineWeight != OdDg::kLineWeightByCell )
764     {
765         nLineWeight = static_cast<int>(uLineWeight);
766         poFeature->SetField("Weight", nLineWeight);
767     }
768 
769     CPLJSONObject uLinkData;
770     CPLJSONArray previousValues;
771 
772     OdRxObjectPtrArray linkages;
773     element->getLinkages(linkages);
774     if( linkages.size() > 0 )
775     {
776         for(unsigned i = 0; i < linkages.size(); ++i)
777         {
778             OdDgAttributeLinkagePtr pLinkage = linkages[i];
779             OdUInt16 primaryId = pLinkage->getPrimaryId();
780             CPLString primaryIdStr = CPLSPrintf("%d", primaryId );
781 
782             OdBinaryData pabyData;
783             pLinkage->getData(pabyData);
784 
785             previousValues = uLinkData.GetArray( primaryIdStr.c_str() );
786             if (!previousValues.IsValid() )
787             {
788                 uLinkData.Add( primaryIdStr.c_str(), CPLJSONArray() );
789                 previousValues = uLinkData.GetArray( primaryIdStr.c_str() );
790             }
791             CPLJSONObject theNewObject = CPLJSONObject();
792             GByte* pData = pabyData.asArrayPtr();
793             int nSize = pabyData.size();
794             theNewObject.Add( "size", nSize );
795             previousValues.Add( theNewObject );
796             switch( primaryId ) {
797                     case OdDgAttributeLinkage::kFRAMME    : // DB Linkage - FRAMME tag data signature
798                     case OdDgAttributeLinkage::kBSI       : // DB Linkage - secondary id link (BSI radix 50)
799                     case OdDgAttributeLinkage::kXBASE     : // DB Linkage - XBase (DBase)
800                     case OdDgAttributeLinkage::kINFORMIX  : // DB Linkage - Informix
801                     case OdDgAttributeLinkage::kINGRES    : // DB Linkage - INGRES
802                     case OdDgAttributeLinkage::kSYBASE    : // DB Linkage - Sybase
803                     case OdDgAttributeLinkage::kODBC      : // DB Linkage - ODBC
804                     case OdDgAttributeLinkage::kOLEDB     : // DB Linkage - OLEDB
805                     case OdDgAttributeLinkage::kORACLE    : // DB Linkage - Oracle
806                     case OdDgAttributeLinkage::kRIS       : // DB Linkage - RIS
807                     {
808                         OdDgDBLinkagePtr dbLinkage = OdDgDBLinkage::cast( pLinkage );
809                         if( !dbLinkage.isNull() )
810                         {
811                             std::string namedType;
812 
813                             switch( dbLinkage->getDBType() )
814                             {
815                             case OdDgDBLinkage::kBSI:
816                                 namedType.assign("BSI");
817                             break;
818                             case OdDgDBLinkage::kFRAMME:
819                                 namedType.assign("FRAMME");
820                             break;
821                             case OdDgDBLinkage::kInformix:
822                                 namedType.assign("Informix");
823                             break;
824                             case OdDgDBLinkage::kIngres:
825                                 namedType.assign("Ingres");
826                             break;
827                             case OdDgDBLinkage::kODBC:
828                                 namedType.assign("ODBC");
829                             break;
830                             case OdDgDBLinkage::kOLEDB:
831                                 namedType.assign("OLE DB");
832                             break;
833                             case OdDgDBLinkage::kOracle:
834                                 namedType.assign("Oracle");
835                             break;
836                             case OdDgDBLinkage::kRIS:
837                                 namedType.assign("RIS");
838                             break;
839                             case OdDgDBLinkage::kSybase:
840                                 namedType.assign("Sybase");
841                             break;
842                             case OdDgDBLinkage::kXbase:
843                                 namedType.assign("xBase");
844                             break;
845                             default:
846                                 namedType.assign("Unknown");
847                             break;
848                             }
849 
850                             theNewObject.Add( "tableId", int( dbLinkage->getTableEntityId() ) );
851                             theNewObject.Add( "MSLink", int( dbLinkage->getMSLink() ) );
852                             theNewObject.Add( "type", namedType );
853                         }
854                     }
855                     break;
856                     case 0x1995: // 0x1995 (6549) IPCC/Portugal
857                     {
858                         theNewObject.Add( "domain", CPLSPrintf("0x%02x", pData[5] ) );
859                         theNewObject.Add( "subdomain", CPLSPrintf("0x%02x", pData[4] ) );
860                         theNewObject.Add( "family", CPLSPrintf("0x%02x", pData[7] ) );
861                         theNewObject.Add( "object", CPLSPrintf("0x%02x", pData[6] ) );
862                         theNewObject.Add( "key", CPLSPrintf("%02x%02x%02x%02x", pData[5], pData[4], pData[7], pData[6] ) );
863                         theNewObject.Add( "type", "IPCC/Portugal" );
864                     }
865                     break;
866                     case OdDgAttributeLinkage::kString: // 0x56d2 (22226):
867                     {
868                         OdDgStringLinkagePtr pStrLinkage = OdDgStringLinkage::cast( pLinkage );
869                         if ( !pStrLinkage.isNull() )
870                         {
871                             theNewObject.Add( "string", ToUTF8(pStrLinkage->getString()).c_str() );
872                             theNewObject.Add( "type", "string" );
873                         }
874                     }
875                     break;
876                     default:
877                     {
878                         CPLJSONArray rawWords;
879                         for( int k=0; k < nSize-1; k+=2 )
880                         {
881                             rawWords.Add( CPLSPrintf("0x%02x%02x", pData[k+1], pData[k] ) );
882                         }
883                         theNewObject.Add( "raw", rawWords );
884                         theNewObject.Add( "type", "unknown" );
885                     }
886                     break;
887             }
888 
889         }
890 
891         poFeature->SetField( "ULink", uLinkData.ToString().c_str() );
892 
893     }
894 
895 /* -------------------------------------------------------------------- */
896 /*      Generate corresponding PEN style.                               */
897 /* -------------------------------------------------------------------- */
898     CPLString osPen;
899 
900     if( nLineStyle == DGNS_SOLID )
901         osPen = "PEN(id:\"ogr-pen-0\"";
902     else if( nLineStyle == DGNS_DOTTED )
903         osPen = "PEN(id:\"ogr-pen-5\"";
904     else if( nLineStyle == DGNS_MEDIUM_DASH )
905         osPen = "PEN(id:\"ogr-pen-2\"";
906     else if( nLineStyle == DGNS_LONG_DASH )
907         osPen = "PEN(id:\"ogr-pen-4\"";
908     else if( nLineStyle == DGNS_DOT_DASH )
909         osPen = "PEN(id:\"ogr-pen-6\"";
910     else if( nLineStyle == DGNS_SHORT_DASH )
911         osPen = "PEN(id:\"ogr-pen-3\"";
912     else if( nLineStyle == DGNS_DASH_DOUBLE_DOT )
913         osPen = "PEN(id:\"ogr-pen-7\"";
914     else if( nLineStyle == DGNS_LONG_DASH_SHORT_DASH )
915         osPen = "PEN(p:\"10px 5px 4px 5px\"";
916     else
917         osPen = "PEN(id:\"ogr-pen-0\"";
918 
919     osPen += osColor;
920 
921     if( nLineWeight > 1 )
922         osPen += CPLSPrintf(",w:%dpx", nLineWeight );
923 
924     osPen += ")";
925 
926     if( EQUAL(pszEntityClassName, "OdDgCellHeader2d") ||
927         EQUAL(pszEntityClassName, "OdDgCellHeader3d") )
928     {
929         bool bDestroyFeature = true;
930         OdDgElementIteratorPtr iterator;
931         if( EQUAL(pszEntityClassName, "OdDgCellHeader2d") )
932         {
933             OdDgCellHeader2dPtr elementCell = OdDgCellHeader2d::cast( element );
934             CPLAssert( !elementCell.isNull() );
935             iterator = elementCell->createIterator();
936         }
937         else
938         {
939             OdDgCellHeader3dPtr elementCell = OdDgCellHeader3d::cast( element );
940             CPLAssert( !elementCell.isNull() );
941             iterator = elementCell->createIterator();
942         }
943         if( !iterator.isNull() )
944         {
945             oVector = CollectSubElements(iterator, level + 1 );
946             int nCountMain = 0;
947             bool bHasHole = false;
948             bool bHasCurve = false;
949             OGRGeometry* poExterior = nullptr;
950             for( size_t i = 0; i < oVector.size(); i++ )
951             {
952                 OGRFeature* poFeat = oVector[i].first;
953                 CPLAssert( poFeat );
954                 OGRGeometry* poGeom = poFeat->GetGeometryRef();
955                 if( poGeom == nullptr )
956                 {
957                     nCountMain = 0;
958                     break;
959                 }
960                 const OGRwkbGeometryType eType =
961                     wkbFlatten(poGeom->getGeometryType());
962                 if( (eType == wkbPolygon || eType == wkbCurvePolygon) &&
963                     poGeom->toCurvePolygon()->getNumInteriorRings() == 0 )
964                 {
965                     if( eType == wkbCurvePolygon )
966                         bHasCurve = true;
967                     if( oVector[i].second )
968                         bHasHole = true;
969                     else
970                     {
971                         poExterior = poGeom;
972                         nCountMain ++;
973                     }
974                 }
975                 else
976                 {
977                     nCountMain = 0;
978                     break;
979                 }
980             }
981             if( nCountMain == 1 && bHasHole )
982             {
983                 bDestroyFeature = false;
984                 OGRCurvePolygon* poCP;
985                 if( bHasCurve )
986                     poCP = new OGRCurvePolygon();
987                 else
988                     poCP = new OGRPolygon();
989                 poCP->addRing(poExterior->toCurvePolygon()->getExteriorRingCurve() );
990                 for( size_t i = 0; i < oVector.size(); i++ )
991                 {
992                     OGRFeature* poFeat = oVector[i].first;
993                     OGRGeometry* poGeom = poFeat->GetGeometryRef();
994                     if( poGeom != poExterior )
995                     {
996                         poCP->addRing(poGeom->toCurvePolygon()->
997                                             getExteriorRingCurve() );
998                     }
999                     delete poFeat;
1000                 }
1001                 oVector.clear();
1002                 poFeature->SetGeometryDirectly(poCP);
1003                 poFeature->SetStyleString( ConsiderBrush(element, osPen) );
1004             }
1005         }
1006         if( bDestroyFeature )
1007         {
1008             delete poFeature;
1009             poFeature = nullptr;
1010         }
1011     }
1012     else if( EQUAL(pszEntityClassName, "OdDgText2d") )
1013     {
1014         OdDgText2dPtr text = OdDgText2d::cast( element );
1015         CPLAssert( !text.isNull() );
1016         ProcessText(poFeature, osColor, text);
1017     }
1018     else if( EQUAL(pszEntityClassName, "OdDgText3d") )
1019     {
1020         OdDgText3dPtr text = OdDgText3d::cast( element );
1021         CPLAssert( !text.isNull() );
1022         ProcessText(poFeature, osColor, text);
1023     }
1024     else if( EQUAL(pszEntityClassName, "OdDgTextNode2d") ||
1025              EQUAL(pszEntityClassName, "OdDgTextNode3d") )
1026     {
1027         OdDgElementIteratorPtr iterator;
1028         if( EQUAL(pszEntityClassName, "OdDgTextNode2d") )
1029         {
1030             OdDgTextNode2dPtr textNode = OdDgTextNode2d::cast( element );
1031             CPLAssert( !textNode.isNull() );
1032             iterator = textNode->createIterator();
1033         }
1034         else
1035         {
1036             OdDgTextNode3dPtr textNode = OdDgTextNode3d::cast( element );
1037             CPLAssert( !textNode.isNull() );
1038             iterator = textNode->createIterator();
1039         }
1040         if( !iterator.isNull() )
1041         {
1042             oVector = CollectSubElements(iterator, level + 1 );
1043         }
1044         delete poFeature;
1045         poFeature = nullptr;
1046     }
1047     else if( EQUAL(pszEntityClassName, "OdDgLine2d") )
1048     {
1049         OdDgLine2dPtr line = OdDgLine2d::cast( element );
1050         CPLAssert( !line.isNull() );
1051         OdGePoint2d pointStart = line->getStartPoint( );
1052         OdGePoint2d pointEnd = line->getEndPoint( );
1053         if( pointStart == pointEnd )
1054         {
1055              poFeature->SetGeometryDirectly(
1056                 new OGRPoint(pointStart.x, pointStart.y) );
1057         }
1058         else
1059         {
1060             OGRLineString* poLS = new OGRLineString();
1061             poLS->setNumPoints(2);
1062             poLS->setPoint(0, pointStart.x, pointStart.y);
1063             poLS->setPoint(1, pointEnd.x, pointEnd.y);
1064             poFeature->SetGeometryDirectly( poLS );
1065             poFeature->SetStyleString(osPen);
1066         }
1067     }
1068     else if( EQUAL(pszEntityClassName, "OdDgLine3d") )
1069     {
1070         OdDgLine3dPtr line = OdDgLine3d::cast( element );
1071         CPLAssert( !line.isNull() );
1072         OdGePoint3d pointStart = line->getStartPoint( );
1073         OdGePoint3d pointEnd = line->getEndPoint( );
1074         if( pointStart == pointEnd )
1075         {
1076              poFeature->SetGeometryDirectly(
1077                 new OGRPoint(pointStart.x, pointStart.y, pointStart.z) );
1078         }
1079         else
1080         {
1081             OGRLineString* poLS = new OGRLineString();
1082             poLS->setNumPoints(2);
1083             poLS->setPoint(0, pointStart.x, pointStart.y, pointStart.z);
1084             poLS->setPoint(1, pointEnd.x, pointEnd.y, pointEnd.z);
1085             poFeature->SetGeometryDirectly( poLS );
1086             poFeature->SetStyleString(osPen);
1087         }
1088     }
1089     else if( EQUAL(pszEntityClassName, "OdDgLineString2d") )
1090     {
1091         OdDgLineString2dPtr line = OdDgLineString2d::cast( element );
1092         CPLAssert( !line.isNull() );
1093         const int nPoints = line->getVerticesCount();
1094 
1095         OGRLineString* poLS = new OGRLineString();
1096         poLS->setNumPoints(nPoints);
1097         for( int i = 0; i < nPoints; i++ )
1098         {
1099             OdGePoint2d point = line->getVertexAt( i );
1100             poLS->setPoint(i, point.x, point.y);
1101         }
1102         poFeature->SetGeometryDirectly( poLS );
1103         poFeature->SetStyleString(osPen);
1104     }
1105     else if( EQUAL(pszEntityClassName, "OdDgLineString3d") )
1106     {
1107         OdDgLineString3dPtr line = OdDgLineString3d::cast( element );
1108         CPLAssert( !line.isNull() );
1109         const int nPoints = line->getVerticesCount();
1110 
1111         OGRLineString* poLS = new OGRLineString();
1112         poLS->setNumPoints(nPoints);
1113         for( int i = 0; i < nPoints; i++ )
1114         {
1115             OdGePoint3d point = line->getVertexAt( i );
1116             poLS->setPoint(i, point.x, point.y, point.z);
1117         }
1118         poFeature->SetGeometryDirectly( poLS );
1119         poFeature->SetStyleString(osPen);
1120     }
1121     else if( EQUAL(pszEntityClassName, "OdDgPointString2d") )
1122     {
1123         OdDgPointString2dPtr string = OdDgPointString2d::cast( element );
1124         CPLAssert( !string.isNull() );
1125         const int nPoints = string->getVerticesCount();
1126 
1127         // Not sure this is the right way to model this
1128         // We lose the rotation per vertex.
1129         OGRMultiPoint* poMP = new OGRMultiPoint();
1130         for( int i = 0; i < nPoints; i++ )
1131         {
1132             OdGePoint2d point = string->getVertexAt( i );
1133             poMP->addGeometryDirectly(new OGRPoint(point.x, point.y));
1134         }
1135         poFeature->SetGeometryDirectly( poMP );
1136     }
1137     else if( EQUAL(pszEntityClassName, "OdDgPointString3d") )
1138     {
1139         OdDgPointString3dPtr string = OdDgPointString3d::cast( element );
1140         CPLAssert( !string.isNull() );
1141         const int nPoints = string->getVerticesCount();
1142 
1143         // Not sure this is the right way to model this
1144         // We lose the rotation per vertex.
1145         OGRMultiPoint* poMP = new OGRMultiPoint();
1146         for( int i = 0; i < nPoints; i++ )
1147         {
1148             OdGePoint3d point = string->getVertexAt( i );
1149             poMP->addGeometryDirectly(new OGRPoint(point.x, point.y, point.z));
1150         }
1151         poFeature->SetGeometryDirectly( poMP );
1152     }
1153     else if( EQUAL(pszEntityClassName, "OdDgMultiline") )
1154     {
1155         // This is a poor approximation since a multiline is a central line
1156         // with parallel lines.
1157         OdDgMultilinePtr line = OdDgMultiline::cast( element );
1158         CPLAssert( !line.isNull() );
1159         const int nPoints = static_cast<int>(line->getPointsCount());
1160 
1161         OGRLineString* poLS = new OGRLineString();
1162         poLS->setNumPoints(nPoints);
1163         for( int i = 0; i < nPoints; i++ )
1164         {
1165             OdDgMultilinePoint point;
1166             OdGePoint3d point3d;
1167             line->getPoint( i, point );
1168             point.getPoint( point3d );
1169             poLS->setPoint(i, point3d.x, point3d.y, point3d.z);
1170         }
1171         poFeature->SetGeometryDirectly( poLS );
1172         poFeature->SetStyleString(osPen);
1173     }
1174     else if( EQUAL(pszEntityClassName, "OdDgArc2d") ||
1175              EQUAL(pszEntityClassName, "OdDgCurve2d") ||
1176              EQUAL(pszEntityClassName, "OdDgBSplineCurve2d") ||
1177              EQUAL(pszEntityClassName, "OdDgEllipse2d") )
1178     {
1179         OdDgCurveElement2dPtr curveElement =
1180                             OdDgCurveElement2d::cast( element );
1181         CPLAssert( !curveElement.isNull() );
1182 
1183         ProcessCurve(poFeature, osPen, curveElement);
1184     }
1185     else if( EQUAL(pszEntityClassName, "OdDgArc3d") ||
1186              EQUAL(pszEntityClassName, "OdDgCurve3d") ||
1187              EQUAL(pszEntityClassName, "OdDgBSplineCurve3d") ||
1188              EQUAL(pszEntityClassName, "OdDgEllipse3d") )
1189     {
1190         OdDgCurveElement3dPtr curveElement =
1191                             OdDgCurveElement3d::cast( element );
1192         CPLAssert( !curveElement.isNull() );
1193 
1194         ProcessCurve(poFeature, osPen, curveElement);
1195     }
1196     else if( EQUAL(pszEntityClassName, "OdDgShape2d") )
1197     {
1198         OdDgShape2dPtr shape = OdDgShape2d::cast( element );
1199         CPLAssert( !shape.isNull() );
1200         bHoleFlag = shape->getHoleFlag();
1201         const int nPoints = shape->getVerticesCount();
1202 
1203         OGRLinearRing* poLS = new OGRLinearRing();
1204         poLS->setNumPoints(nPoints);
1205         for( int i = 0; i < nPoints; i++ )
1206         {
1207             OdGePoint2d point = shape->getVertexAt( i );
1208             poLS->setPoint(i, point.x, point.y);
1209         }
1210         OGRPolygon* poPoly = new OGRPolygon();
1211         poPoly->addRingDirectly(poLS);
1212         poFeature->SetGeometryDirectly( poPoly );
1213         poFeature->SetStyleString(ConsiderBrush(element, osPen));
1214     }
1215     else if( EQUAL(pszEntityClassName, "OdDgShape3d") )
1216     {
1217         OdDgShape3dPtr shape = OdDgShape3d::cast( element );
1218         CPLAssert( !shape.isNull() );
1219         bHoleFlag = shape->getHoleFlag();
1220         const int nPoints = shape->getVerticesCount();
1221 
1222         OGRLinearRing* poLS = new OGRLinearRing();
1223         poLS->setNumPoints(nPoints);
1224         for( int i = 0; i < nPoints; i++ )
1225         {
1226             OdGePoint3d point = shape->getVertexAt( i );
1227             poLS->setPoint(i, point.x, point.y, point.z);
1228         }
1229         OGRPolygon* poPoly = new OGRPolygon();
1230         poPoly->addRingDirectly(poLS);
1231         poFeature->SetGeometryDirectly( poPoly );
1232         poFeature->SetStyleString(ConsiderBrush(element, osPen));
1233     }
1234     else if( EQUAL(pszEntityClassName, "OdDgComplexString") )
1235     {
1236         OdDgComplexStringPtr complex = OdDgComplexString::cast( element );
1237         CPLAssert( !complex.isNull() );
1238 
1239         OdDgElementIteratorPtr iterator = complex->createIterator();
1240         if( !iterator.isNull() )
1241         {
1242             std::vector<tPairFeatureHoleFlag> oVectorSubElts =
1243                                 CollectSubElements(iterator, level + 1 );
1244 
1245             // First pass to determine if we have non-linear pieces.
1246             bool bHasCurves = false;
1247             bool bIsClosed = false;
1248             bool bIsContiguous = IsContiguous(oVectorSubElts, bHasCurves, bIsClosed );
1249 
1250             if( bIsContiguous && bHasCurves )
1251             {
1252                 OGRCompoundCurve* poCC = new OGRCompoundCurve();
1253 
1254                 // Second pass to aggregate the geometries.
1255                 for( size_t i = 0; i < oVectorSubElts.size(); i++ )
1256                 {
1257                     OGRGeometry* poGeom =
1258                             oVectorSubElts[i].first->GetGeometryRef();
1259                     if( poGeom != nullptr )
1260                     {
1261                         OGRwkbGeometryType eType =
1262                                         wkbFlatten(poGeom->getGeometryType());
1263                         if( eType == wkbCircularString || eType == wkbLineString )
1264                         {
1265                             poCC->addCurve( poGeom->toCurve(),
1266                                             CONTIGUITY_TOLERANCE );
1267                         }
1268                     }
1269                     delete oVectorSubElts[i].first;
1270                 }
1271 
1272                 poFeature->SetGeometryDirectly( poCC );
1273             }
1274             else
1275             {
1276                 OGRMultiCurve* poMC;
1277                 if( bHasCurves )
1278                     poMC = new OGRMultiCurve();
1279                 else
1280                     poMC = new OGRMultiLineString();
1281 
1282                 // Second pass to aggregate the geometries.
1283                 for( size_t i = 0; i < oVectorSubElts.size(); i++ )
1284                 {
1285                     OGRGeometry* poGeom =
1286                                     oVectorSubElts[i].first->GetGeometryRef();
1287                     if( poGeom != nullptr )
1288                     {
1289                         OGRwkbGeometryType eType =
1290                                         wkbFlatten(poGeom->getGeometryType());
1291                         if( eType == wkbCircularString || eType == wkbLineString )
1292                         {
1293                             poMC->addGeometry( poGeom );
1294                         }
1295                     }
1296                     delete oVectorSubElts[i].first;
1297                 }
1298 
1299                 poFeature->SetGeometryDirectly( poMC );
1300             }
1301             poFeature->SetStyleString( osPen );
1302         }
1303     }
1304     else if( EQUAL(pszEntityClassName, "OdDgComplexShape") )
1305     {
1306         OdDgComplexCurvePtr complex = OdDgComplexCurve::cast( element );
1307         CPLAssert( !complex.isNull() );
1308 
1309         OdDgComplexShapePtr complexShape = OdDgComplexShape::cast( element );
1310         CPLAssert( !complexShape.isNull() );
1311         bHoleFlag = complexShape->getHoleFlag();
1312 
1313         OdDgElementIteratorPtr iterator = complex->createIterator();
1314         if( !iterator.isNull() )
1315         {
1316             std::vector<tPairFeatureHoleFlag> oVectorSubElts =
1317                                 CollectSubElements(iterator, level + 1 );
1318 
1319             // First pass to determine if we have non-linear pieces.
1320             bool bHasCurves = false;
1321             bool bIsClosed = false;
1322             bool bIsContiguous = IsContiguous(oVectorSubElts, bHasCurves, bIsClosed );
1323 
1324             if( bIsContiguous && bIsClosed )
1325             {
1326                 OGRCurvePolygon* poCP;
1327                 OGRCompoundCurve* poCC = nullptr;
1328                 OGRLinearRing* poLR = nullptr;
1329 
1330                 if( bHasCurves )
1331                 {
1332                     poCP = new OGRCurvePolygon();
1333                     poCC = new OGRCompoundCurve();
1334                 }
1335                 else
1336                 {
1337                     poCP = new OGRPolygon();
1338                     poLR = new OGRLinearRing();
1339                 }
1340 
1341                 // Second pass to aggregate the geometries.
1342                 for( size_t i = 0; i < oVectorSubElts.size(); i++ )
1343                 {
1344                     OGRGeometry* poGeom =
1345                             oVectorSubElts[i].first->GetGeometryRef();
1346                     if( poGeom != nullptr )
1347                     {
1348                         OGRwkbGeometryType eType =
1349                                     wkbFlatten(poGeom->getGeometryType());
1350                         if( poCC != nullptr )
1351                         {
1352                             poCC->addCurve( poGeom->toCurve(),
1353                                             CONTIGUITY_TOLERANCE );
1354                         }
1355                         else if( eType == wkbLineString )
1356                         {
1357                             poLR->addSubLineString(poGeom->toLineString(),
1358                                 poLR->getNumPoints() == 0 ? 0 : 1 );
1359                         }
1360                     }
1361                     delete oVectorSubElts[i].first;
1362                 }
1363 
1364                 poCP->addRingDirectly( ( bHasCurves ) ?
1365                     poCC->toCurve() :
1366                     poLR->toCurve() );
1367 
1368                 poFeature->SetGeometryDirectly( poCP );
1369             }
1370             else
1371             {
1372                 OGRGeometryCollection oGC;
1373                 for( size_t i = 0; i < oVectorSubElts.size(); i++ )
1374                 {
1375                     OGRGeometry* poGeom =
1376                                     oVectorSubElts[i].first->StealGeometry();
1377                     if( poGeom != nullptr )
1378                     {
1379                         OGRwkbGeometryType eType =
1380                                         wkbFlatten(poGeom->getGeometryType());
1381                         if( eType == wkbCircularString )
1382                         {
1383                             oGC.addGeometryDirectly(
1384                                 OGRGeometryFactory::forceToLineString(poGeom) );
1385                         }
1386                         else if( eType == wkbLineString )
1387                         {
1388                             oGC.addGeometryDirectly( poGeom );
1389                         }
1390                         else
1391                         {
1392                             delete poGeom;
1393                         }
1394                     }
1395                     delete oVectorSubElts[i].first;
1396                 }
1397 
1398                 // Try to assemble into polygon geometry.
1399                 OGRGeometry* poGeom = reinterpret_cast<OGRGeometry *>(
1400                     OGRBuildPolygonFromEdges(
1401                       reinterpret_cast<OGRGeometryH>( &oGC ),
1402                       TRUE, TRUE, CONTIGUITY_TOLERANCE, nullptr ) );
1403                 poGeom->setCoordinateDimension( oGC.getCoordinateDimension() );
1404                 poFeature->SetGeometryDirectly( poGeom );
1405 
1406             }
1407             poFeature->SetStyleString( ConsiderBrush( element, osPen) );
1408         }
1409     }
1410     else if( EQUAL(pszEntityClassName, "OdDgSharedCellReference") )
1411     {
1412         OdDgSharedCellReferencePtr ref =
1413                             OdDgSharedCellReference::cast( element );
1414         CPLAssert( !ref.isNull() );
1415         OdGePoint3d point = ref->getOrigin();
1416         poFeature->SetField( "Text",
1417                     ToUTF8(ref->getDefinitionName()).c_str() );
1418         poFeature->SetGeometryDirectly(
1419                 new OGRPoint(point.x, point.y, point.z) );
1420     }
1421     else
1422     {
1423         if( m_aoSetIgnoredFeatureClasses.find(pszEntityClassName) ==
1424                                         m_aoSetIgnoredFeatureClasses.end() )
1425         {
1426             m_aoSetIgnoredFeatureClasses.insert(pszEntityClassName);
1427             CPLDebug("DGNV8", "Unhandled class %s for, at least, "
1428                      "feature " CPL_FRMT_GIB,
1429                      pszEntityClassName, poFeature->GetFID());
1430         }
1431     }
1432 
1433     if( poFeature != nullptr )
1434         oVector.push_back( tPairFeatureHoleFlag(poFeature, bHoleFlag) );
1435 
1436     return oVector;
1437 }
1438 
1439 /************************************************************************/
1440 /*                    GetNextUnfilteredFeature()                        */
1441 /************************************************************************/
1442 
GetNextUnfilteredFeature()1443 OGRFeature *OGRDGNV8Layer::GetNextUnfilteredFeature()
1444 
1445 {
1446     while( true )
1447     {
1448         if( m_nIdxInPendingFeatures < m_aoPendingFeatures.size() )
1449         {
1450             OGRFeature* poFeature =
1451                 m_aoPendingFeatures[m_nIdxInPendingFeatures].first;
1452             m_aoPendingFeatures[m_nIdxInPendingFeatures].first = nullptr;
1453             m_nIdxInPendingFeatures ++;
1454             return poFeature;
1455         }
1456 
1457         if( m_pIterator.isNull() )
1458             return nullptr;
1459 
1460         while( true )
1461         {
1462             if( m_pIterator->done() )
1463                 return nullptr;
1464             OdRxObjectPtr object = m_pIterator->item().openObject();
1465             m_pIterator->step();
1466             OdDgGraphicsElementPtr element =
1467                             OdDgGraphicsElement::cast( object );
1468             if (element.isNull())
1469                 continue;
1470 
1471             m_aoPendingFeatures = ProcessElement(element);
1472             m_nIdxInPendingFeatures = 0;
1473 
1474             break;
1475         }
1476     }
1477 }
1478 
1479 /************************************************************************/
1480 /*                           GetNextFeature()                           */
1481 /************************************************************************/
1482 
GetNextFeature()1483 OGRFeature *OGRDGNV8Layer::GetNextFeature()
1484 
1485 {
1486     while( true )
1487     {
1488         OGRFeature* poFeature = GetNextUnfilteredFeature();
1489         if( poFeature == nullptr )
1490             break;
1491         if( poFeature->GetGeometryRef() == nullptr )
1492         {
1493             delete poFeature;
1494             continue;
1495         }
1496 
1497         if( (m_poAttrQuery == nullptr
1498              || m_poAttrQuery->Evaluate( poFeature ))
1499             && FilterGeometry( poFeature->GetGeometryRef() ) )
1500             return poFeature;
1501 
1502         delete poFeature;
1503     }
1504     return nullptr;
1505 }
1506 
1507 /************************************************************************/
1508 /*                        GetFeatureInternal()                          */
1509 /************************************************************************/
1510 
GetFeatureInternal(GIntBig nFID,OdDg::OpenMode openMode)1511 OdDgGraphicsElementPtr OGRDGNV8Layer::GetFeatureInternal(GIntBig nFID,
1512                                                   OdDg::OpenMode openMode)
1513 {
1514     if( nFID < 0 )
1515         return OdDgGraphicsElementPtr();
1516     const OdDbHandle handle( static_cast<OdUInt64>(nFID) );
1517     const OdDgElementId id = m_pModel->database()->getElementId(handle);
1518     OdRxObjectPtr object = id.openObject(openMode);
1519     OdDgGraphicsElementPtr element = OdDgGraphicsElement::cast( object );
1520     if (element.isNull() || element->ownerId() != m_pModel->elementId() )
1521         return OdDgGraphicsElementPtr();
1522     return element;
1523 }
1524 
1525 /************************************************************************/
1526 /*                            GetFeature()                              */
1527 /************************************************************************/
1528 
GetFeature(GIntBig nFID)1529 OGRFeature *OGRDGNV8Layer::GetFeature(GIntBig nFID)
1530 {
1531     OdDgGraphicsElementPtr element = GetFeatureInternal(nFID, OdDg::kForRead);
1532     if( element.isNull() )
1533         return nullptr;
1534     std::vector<tPairFeatureHoleFlag> oVector = ProcessElement(element);
1535     // Only return a feature if and only if we have a single element
1536     if( oVector.empty() )
1537         return nullptr;
1538     if( oVector.size() > 1 )
1539     {
1540         for( size_t i = 0; i < oVector.size(); i++ )
1541             delete oVector[i].first;
1542         return nullptr;
1543     }
1544     return oVector[0].first;
1545 }
1546 
1547 /************************************************************************/
1548 /*                          DeleteFeature()                             */
1549 /************************************************************************/
1550 
DeleteFeature(GIntBig nFID)1551 OGRErr OGRDGNV8Layer::DeleteFeature(GIntBig nFID)
1552 {
1553     if( !m_poDS->GetUpdate() )
1554     {
1555         CPLError( CE_Failure, CPLE_AppDefined,
1556                   "Attempt to delete feature on read-only DGN file." );
1557         return OGRERR_FAILURE;
1558     }
1559 
1560     OdDgGraphicsElementPtr element = GetFeatureInternal(nFID,
1561                                                         OdDg::kForWrite);
1562     if( element.isNull() )
1563         return OGRERR_FAILURE;
1564     try
1565     {
1566         element->erase(true);
1567     }
1568     catch (const OdError& e)
1569     {
1570         CPLError(CE_Failure, CPLE_AppDefined,
1571                  "Teigha DGN error occurred: %s",
1572                  ToUTF8(e.description()).c_str());
1573         return OGRERR_FAILURE;
1574     }
1575     catch (const std::exception &exc)
1576     {
1577         CPLError(CE_Failure, CPLE_AppDefined,
1578                  "std::exception occurred: %s", exc.what());
1579         return OGRERR_FAILURE;
1580     }
1581     catch (...)
1582     {
1583         CPLError(CE_Failure, CPLE_AppDefined,
1584                  "Unknown exception occurred");
1585         return OGRERR_FAILURE;
1586     }
1587     m_poDS->SetModified();
1588     return OGRERR_NONE;
1589 }
1590 
1591 
1592 /************************************************************************/
1593 /*                             GetExtent()                              */
1594 /************************************************************************/
1595 
GetExtent(OGREnvelope * psExtent,int bForce)1596 OGRErr OGRDGNV8Layer::GetExtent( OGREnvelope *psExtent, int bForce )
1597 {
1598     OdDgModel::StorageUnitDescription description;
1599     m_pModel->getStorageUnit( description );
1600     OdDgElementIteratorPtr iterator =
1601                         m_pModel->createGraphicsElementsIterator();
1602     bool bValid = false;
1603     while( true )
1604     {
1605         if( iterator.isNull() || iterator->done() )
1606             break;
1607         OdRxObjectPtr object = iterator->item().openObject();
1608         iterator->step();
1609         OdDgGraphicsElementPtr element = OdDgGraphicsElement::cast( object );
1610         if (element.isNull())
1611             continue;
1612         OdDgGraphicsElementPEPtr pElementPE =
1613             OdDgGraphicsElementPEPtr(OdRxObjectPtr(element));
1614         if( pElementPE.isNull() )
1615             continue;
1616         OdGeExtents3d savedExtent;
1617         if( pElementPE->getRange( element, savedExtent ) == eOk )
1618         {
1619             OdGePoint3d min = savedExtent.minPoint();
1620             OdGePoint3d max = savedExtent.maxPoint();
1621             if( !bValid )
1622             {
1623                 psExtent->MinX = min.x / description.m_uorPerStorageUnit;
1624                 psExtent->MinY = min.y / description.m_uorPerStorageUnit;
1625                 psExtent->MaxX = max.x / description.m_uorPerStorageUnit;
1626                 psExtent->MaxY = max.y / description.m_uorPerStorageUnit;
1627                 bValid = true;
1628             }
1629             else
1630             {
1631                 psExtent->MinX = std::min(psExtent->MinX,
1632                     min.x / description.m_uorPerStorageUnit);
1633                 psExtent->MinY = std::min(psExtent->MinY,
1634                     min.y / description.m_uorPerStorageUnit);
1635                 psExtent->MaxX = std::max(psExtent->MaxX,
1636                     max.x / description.m_uorPerStorageUnit);
1637                 psExtent->MaxY = std::max(psExtent->MaxY,
1638                     max.y / description.m_uorPerStorageUnit);
1639             }
1640         }
1641     }
1642     if( bValid )
1643         return OGRERR_NONE;
1644     return OGRLayer::GetExtent(psExtent, bForce);
1645 }
1646 
1647 
1648 /************************************************************************/
1649 /*                           TestCapability()                           */
1650 /************************************************************************/
1651 
TestCapability(const char * pszCap)1652 int OGRDGNV8Layer::TestCapability( const char * pszCap )
1653 
1654 {
1655     if( EQUAL(pszCap,OLCRandomRead) )
1656         return TRUE;
1657     else if( EQUAL(pszCap,OLCStringsAsUTF8) )
1658         return TRUE;
1659     else if( EQUAL(pszCap,OLCSequentialWrite) ||
1660              EQUAL(pszCap,OLCDeleteFeature) )
1661         return m_poDS->GetUpdate();
1662     else if( EQUAL(pszCap,OLCCurveGeometries) )
1663         return TRUE;
1664 
1665     return FALSE;
1666 }
1667 
1668 
1669 /************************************************************************/
1670 /*                           ICreateFeature()                            */
1671 /*                                                                      */
1672 /*      Create a new feature and write to file.                         */
1673 /************************************************************************/
1674 
ICreateFeature(OGRFeature * poFeature)1675 OGRErr OGRDGNV8Layer::ICreateFeature( OGRFeature *poFeature )
1676 
1677 {
1678     if( !m_poDS->GetUpdate() )
1679     {
1680         CPLError( CE_Failure, CPLE_AppDefined,
1681                   "Attempt to create feature on read-only DGN file." );
1682         return OGRERR_FAILURE;
1683     }
1684 
1685     if( poFeature->GetGeometryRef() == nullptr )
1686     {
1687         CPLError( CE_Failure, CPLE_AppDefined,
1688                   "Features with empty, geometry collection geometries not "
1689                   "supported in DGN format." );
1690         return OGRERR_FAILURE;
1691     }
1692 
1693     try
1694     {
1695         OdDgGraphicsElementPtr element = CreateGraphicsElement(
1696                                 poFeature, poFeature->GetGeometryRef() );
1697         if( element.isNull() )
1698             return OGRERR_FAILURE;
1699         m_pModel->addElement(element);
1700         poFeature->SetFID(
1701             static_cast<GIntBig>(
1702                 static_cast<OdUInt64>(element->elementId().getHandle()) ) );
1703     }
1704     catch (const OdError& e)
1705     {
1706         CPLError(CE_Failure, CPLE_AppDefined,
1707                  "Teigha DGN error occurred: %s",
1708                  ToUTF8(e.description()).c_str());
1709         return OGRERR_FAILURE;
1710     }
1711     catch (const std::exception &exc)
1712     {
1713         CPLError(CE_Failure, CPLE_AppDefined,
1714                  "std::exception occurred: %s", exc.what());
1715         return OGRERR_FAILURE;
1716     }
1717     catch (...)
1718     {
1719         CPLError(CE_Failure, CPLE_AppDefined,
1720                  "Unknown exception occurred");
1721         return OGRERR_FAILURE;
1722     }
1723 
1724     m_poDS->SetModified();
1725     return OGRERR_NONE;
1726 }
1727 
1728 /************************************************************************/
1729 /*                              GetTool()                               */
1730 /************************************************************************/
1731 
GetTool(OGRFeature * poFeature,OGRSTClassId eClassId)1732 static OGRStyleTool* GetTool( OGRFeature* poFeature, OGRSTClassId eClassId )
1733 {
1734 
1735     OGRStyleMgr oMgr;
1736     oMgr.InitFromFeature( poFeature );
1737     for( int i=0; i<oMgr.GetPartCount(); i++)
1738     {
1739         OGRStyleTool* poTool = oMgr.GetPart( i );
1740         if( poTool == nullptr || poTool->GetType() != eClassId)
1741         {
1742             delete poTool;
1743         }
1744         else
1745         {
1746             return poTool;
1747         }
1748     }
1749     return nullptr;
1750 }
1751 
1752 /************************************************************************/
1753 /*                           TranslateLabel()                           */
1754 /************************************************************************/
1755 
TranslateLabel(OGRFeature * poFeature,OGRPoint * poPoint)1756 OdDgGraphicsElementPtr OGRDGNV8Layer::TranslateLabel(
1757                                     OGRFeature *poFeature, OGRPoint *poPoint )
1758 
1759 {
1760     const char *pszText = poFeature->GetFieldAsString( "Text" );
1761 
1762     OGRStyleLabel *poLabel = static_cast<OGRStyleLabel*>(
1763                                             GetTool(poFeature, OGRSTCLabel));
1764 
1765     OdDgText2dPtr text = OdDgText2d::createObject();
1766     OdGePoint2d point;
1767     point.x = poPoint->getX();
1768     point.y = poPoint->getY();
1769     text->setOrigin(point);
1770 
1771     double dfHeightMultiplier = 1.0;
1772     if( poLabel != nullptr )
1773     {
1774         GBool bDefault;
1775 
1776         if( poLabel->TextString(bDefault) != nullptr && !bDefault )
1777             pszText = poLabel->TextString(bDefault);
1778 
1779         const double dfRotation = poLabel->Angle(bDefault);
1780         text->setRotation(dfRotation * DEG_TO_RAD);
1781 
1782         poLabel->SetUnit(OGRSTUMM);
1783         double dfVal = poLabel->Size( bDefault );
1784         if( !bDefault  )
1785             dfHeightMultiplier = dfVal/1000.0;
1786 
1787         /* get font id */
1788         const char *pszFontName = poLabel->FontName( bDefault );
1789         if( !bDefault && pszFontName != nullptr )
1790         {
1791             OdDgFontTablePtr pFontTable =
1792                 m_pModel->database()->getFontTable(OdDg::kForRead);
1793             OdDgElementId idFont = pFontTable->getAt(
1794                             OGRDGNV8DataSource::FromUTF8(pszFontName) );
1795             if( !idFont.isNull() )
1796             {
1797                 OdDgFontTableRecordPtr pFont =
1798                     idFont.openObject(OdDg::kForRead);
1799                 OdUInt32 uFontEntryId = pFont->getNumber();
1800                 text->setFontEntryId(uFontEntryId);
1801             }
1802         }
1803 
1804         int nAnchor = poLabel->Anchor(bDefault);
1805         if( !bDefault )
1806             text->setJustification( GetAnchorPositionFromOGR( nAnchor ) );
1807     }
1808 
1809     text->setHeightMultiplier( dfHeightMultiplier );
1810     text->setLengthMultiplier( text->getHeightMultiplier() ); // FIXME ??
1811     text->setText( OGRDGNV8DataSource::FromUTF8(pszText) );
1812 
1813     if( poLabel )
1814         delete poLabel;
1815     return text;
1816 }
1817 
1818 /************************************************************************/
1819 /*                       GetColorFromString()                           */
1820 /************************************************************************/
1821 
GetColorFromString(const char * pszColor)1822 int OGRDGNV8Layer::GetColorFromString(const char* pszColor)
1823 {
1824     unsigned int nRed = 0;
1825     unsigned int nGreen = 0;
1826     unsigned int nBlue = 0;
1827     const int nCount =
1828         sscanf(pszColor, "#%2x%2x%2x", &nRed, &nGreen, &nBlue);
1829     if( nCount == 3 )
1830     {
1831         OdUInt32 nIdx = OdDgColorTable::getColorIndexByRGB(
1832             m_poDS->GetDb(), ODRGB( nRed, nGreen ,nBlue ) );
1833         return static_cast<int>(nIdx);
1834     }
1835     else
1836     {
1837         return -1;
1838     }
1839 }
1840 
1841 /************************************************************************/
1842 /*                       AttachFillLinkage()                            */
1843 /************************************************************************/
1844 
AttachFillLinkage(OGRFeature * poFeature,OdDgGraphicsElementPtr element)1845 void OGRDGNV8Layer::AttachFillLinkage( OGRFeature* poFeature,
1846                                        OdDgGraphicsElementPtr element )
1847 {
1848     const char* pszStyle = poFeature->GetStyleString();
1849     if( pszStyle != nullptr && strstr(pszStyle, "BRUSH") != nullptr )
1850     {
1851         OGRStyleBrush* poBrush = static_cast<OGRStyleBrush*>(
1852                                             GetTool(poFeature, OGRSTCBrush));
1853         if( poBrush != nullptr )
1854         {
1855             GBool bDefault;
1856             const char* pszColor = poBrush->ForeColor(bDefault);
1857             if( pszColor && !bDefault )
1858             {
1859                 const int nIdx = GetColorFromString(pszColor);
1860                 if( nIdx >= 0 )
1861                 {
1862                     OdDgFillColorLinkagePtr fillColor =
1863                             OdDgFillColorLinkage::createObject();
1864                     fillColor->setColorIndex( nIdx );
1865                     element->addLinkage( fillColor->getPrimaryId(),
1866                                          fillColor.get() );
1867                 }
1868             }
1869 
1870             delete poBrush;
1871         }
1872     }
1873 }
1874 
1875 /************************************************************************/
1876 /*                       AttachCommonAttributes()                       */
1877 /************************************************************************/
1878 
AttachCommonAttributes(OGRFeature * poFeature,OdDgGraphicsElementPtr element)1879 void OGRDGNV8Layer::AttachCommonAttributes( OGRFeature *poFeature,
1880                                             OdDgGraphicsElementPtr element )
1881 {
1882     const int nLevel = poFeature->GetFieldAsInteger( "Level" );
1883     const int nGraphicGroup = poFeature->GetFieldAsInteger( "GraphicGroup" );
1884     const int nWeight = poFeature->GetFieldAsInteger( "Weight" );
1885     const int nStyle = poFeature->GetFieldAsInteger( "Style" );
1886 
1887     element->setLevelEntryId(nLevel);
1888     element->setGraphicsGroupEntryId(nGraphicGroup);
1889 
1890     const int nColorIndexField = poFeature->GetFieldIndex("ColorIndex");
1891     if( poFeature->IsFieldSetAndNotNull(nColorIndexField) )
1892     {
1893         const int nColor = poFeature->GetFieldAsInteger(nColorIndexField);
1894         element->setColorIndex(nColor);
1895     }
1896     else
1897     {
1898         const char* pszStyle = poFeature->GetStyleString();
1899         if( pszStyle != nullptr && strstr(pszStyle, "PEN") != nullptr )
1900         {
1901             OGRStylePen* poPen = static_cast<OGRStylePen*>(
1902                                             GetTool(poFeature, OGRSTCPen));
1903             if( poPen != nullptr )
1904             {
1905                 GBool bDefault;
1906                 const char* pszColor = poPen->Color(bDefault);
1907                 if( pszColor && !bDefault )
1908                 {
1909                     const int nIdx = GetColorFromString(pszColor);
1910                     if( nIdx >= 0 )
1911                     {
1912                         element->setColorIndex(nIdx);
1913                     }
1914                 }
1915                 delete poPen;
1916             }
1917         }
1918         else if( pszStyle != nullptr && strstr(pszStyle, "LABEL") != nullptr )
1919         {
1920             OGRStyleLabel *poLabel = static_cast<OGRStyleLabel*>(
1921                                             GetTool(poFeature, OGRSTCLabel));
1922             if( poLabel != nullptr )
1923             {
1924                 GBool bDefault;
1925                 const char* pszColor = poLabel->ForeColor(bDefault);
1926                 if( pszColor && !bDefault )
1927                 {
1928                     const int nIdx = GetColorFromString(pszColor);
1929                     if( nIdx >= 0 )
1930                     {
1931                         element->setColorIndex(nIdx);
1932                     }
1933                 }
1934 
1935                 delete poLabel;
1936             }
1937         }
1938     }
1939 
1940     element->setLineStyleEntryId(nStyle);
1941     element->setLineWeight(nWeight);
1942 }
1943 
1944 /************************************************************************/
1945 /*                          AddToComplexCurve()                         */
1946 /************************************************************************/
1947 
AddToComplexCurve(OGRFeature * poFeature,OGRCircularString * poCS,OdDgComplexCurvePtr complexCurve)1948 void OGRDGNV8Layer::AddToComplexCurve( OGRFeature* poFeature,
1949                                        OGRCircularString* poCS,
1950                                        OdDgComplexCurvePtr complexCurve )
1951 {
1952     for( int i = 0; i + 2 < poCS->getNumPoints(); i+= 2 )
1953     {
1954         double R, cx, cy;
1955         double alpha0, alpha1, alpha2;
1956         if( OGRGeometryFactory::GetCurveParameters(
1957                 poCS->getX(i),
1958                 poCS->getY(i),
1959                 poCS->getX(i+1),
1960                 poCS->getY(i+1),
1961                 poCS->getX(i+2),
1962                 poCS->getY(i+2),
1963                 R, cx, cy, alpha0, alpha1, alpha2) )
1964         {
1965             OdDgArc2dPtr arc = OdDgArc2d::createObject();
1966             arc->setPrimaryAxis(R);
1967             arc->setSecondaryAxis(R);
1968             OdGePoint2d point;
1969             point.x = cx;
1970             point.y = cy;
1971             arc->setOrigin(point);
1972             arc->setStartAngle(alpha0); // already in radians
1973             arc->setSweepAngle(alpha2 - alpha0);
1974             AttachCommonAttributes(poFeature, arc);
1975             complexCurve->add(arc);
1976         }
1977     }
1978 }
1979 
1980 /************************************************************************/
1981 /*                         AddToComplexCurve()                          */
1982 /************************************************************************/
1983 
AddToComplexCurve(OGRFeature * poFeature,OGRCompoundCurve * poCC,OdDgComplexCurvePtr complexCurve)1984 void OGRDGNV8Layer::AddToComplexCurve( OGRFeature* poFeature,
1985                                        OGRCompoundCurve* poCC,
1986                                        OdDgComplexCurvePtr complexCurve )
1987 {
1988     for( int iCurve = 0; iCurve < poCC->getNumCurves(); ++iCurve )
1989     {
1990         OGRCurve* poCurve = poCC->getCurve(iCurve);
1991         OGRwkbGeometryType eType = wkbFlatten(poCurve->getGeometryType());
1992         if( eType == wkbLineString || OGR_GT_HasZ(eType) )
1993         {
1994             complexCurve->add( CreateGraphicsElement(poFeature, poCurve) );
1995         }
1996         else if( eType == wkbCircularString )
1997         {
1998             OGRCircularString* poCS = poCurve->toCircularString();
1999             AddToComplexCurve(poFeature, poCS, complexCurve);
2000         }
2001         else
2002         {
2003             CPLAssert(false);
2004         }
2005     }
2006 }
2007 
2008 /************************************************************************/
2009 /*                        CreateShapeFromLS()                           */
2010 /************************************************************************/
2011 
2012 static
CreateShapeFromLS(OGRLineString * poLS,bool bHbit=false)2013 OdDgGraphicsElementPtr CreateShapeFromLS( OGRLineString* poLS,
2014                                           bool bHbit = false )
2015 {
2016     if( OGR_GT_HasZ(poLS->getGeometryType()) )
2017     {
2018         OdDgShape3dPtr shape = OdDgShape3d::createObject();
2019         for( int i = 0; i < poLS->getNumPoints(); i++ )
2020         {
2021             OGRPoint ogrPoint;
2022             OdGePoint3d point;
2023             poLS->getPoint(i, &ogrPoint);
2024             point.x = ogrPoint.getX();
2025             point.y = ogrPoint.getY();
2026             point.z = ogrPoint.getZ();
2027             shape->addVertex(point);
2028         }
2029         shape->setHbitFlag(bHbit);
2030         return shape;
2031     }
2032     else
2033     {
2034         OdDgShape2dPtr shape = OdDgShape2d::createObject();
2035         for( int i = 0; i < poLS->getNumPoints(); i++ )
2036         {
2037             OGRPoint ogrPoint;
2038             OdGePoint2d point;
2039             poLS->getPoint(i, &ogrPoint);
2040             point.x = ogrPoint.getX();
2041             point.y = ogrPoint.getY();
2042             shape->addVertex(point);
2043         }
2044         shape->setHbitFlag(bHbit);
2045         return shape;
2046     }
2047 }
2048 
2049 /************************************************************************/
2050 /*                           CreateShape()                              */
2051 /************************************************************************/
2052 
CreateShape(OGRFeature * poFeature,OGRCurve * poCurve,bool bIsHole)2053 OdDgGraphicsElementPtr OGRDGNV8Layer::CreateShape( OGRFeature* poFeature,
2054                                                    OGRCurve* poCurve,
2055                                                    bool bIsHole )
2056 {
2057     OdDgGraphicsElementPtr element;
2058     OGRwkbGeometryType eType = wkbFlatten(poCurve->getGeometryType());
2059     if( eType == wkbLineString )
2060     {
2061         OGRLineString* poLS = poCurve->toLineString();
2062         element = CreateShapeFromLS(poLS, bIsHole);
2063     }
2064     else if( eType == wkbCircularString )
2065     {
2066         OdDgComplexShapePtr complexShape = OdDgComplexShape::createObject();
2067         complexShape->setHbitFlag(bIsHole);
2068         OGRCircularString* poCS = poCurve->toCircularString();
2069         AddToComplexCurve(poFeature, poCS, complexShape);
2070         element = complexShape;
2071     }
2072     else if( eType == wkbCompoundCurve )
2073     {
2074         OdDgComplexShapePtr complexShape = OdDgComplexShape::createObject();
2075         complexShape->setHbitFlag(bIsHole);
2076         OGRCompoundCurve* poCC = poCurve->toCompoundCurve();
2077         AddToComplexCurve( poFeature, poCC, complexShape );
2078         element = complexShape;
2079     }
2080 
2081     if( !bIsHole )
2082         AttachFillLinkage( poFeature, element );
2083 
2084     return element;
2085 }
2086 
2087 /************************************************************************/
2088 /*                           IsFullCircle()                             */
2089 /************************************************************************/
2090 
IsFullCircle(OGRCircularString * poCS,double & cx,double & cy,double & R)2091 static bool IsFullCircle( OGRCircularString* poCS,
2092                           double& cx, double& cy,
2093                           double& R )
2094 {
2095     if( poCS->getNumPoints() == 3 && poCS->get_IsClosed() )
2096     {
2097         const double x0 = poCS->getX(0);
2098         const double y0 = poCS->getY(0);
2099         const double x1 = poCS->getX(1);
2100         const double y1 = poCS->getY(1);
2101         cx = (x0 + x1) / 2;
2102         cy = (y0 + y1) / 2;
2103         R = sqrt((x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy));
2104         return true;
2105     }
2106     // Full circle defined by 2 arcs?
2107     else if( poCS->getNumPoints() == 5 && poCS->get_IsClosed() )
2108     {
2109         double R_1 = 0.0;
2110         double cx_1 = 0.0;
2111         double cy_1 = 0.0;
2112         double alpha0_1 = 0.0;
2113         double alpha1_1 = 0.0;
2114         double alpha2_1 = 0.0;
2115         double R_2 = 0.0;
2116         double cx_2 = 0.0;
2117         double cy_2 = 0.0;
2118         double alpha0_2 = 0.0;
2119         double alpha1_2 = 0.0;
2120         double alpha2_2 = 0.0;
2121         if( OGRGeometryFactory::GetCurveParameters(
2122                 poCS->getX(0), poCS->getY(0),
2123                 poCS->getX(1), poCS->getY(1),
2124                 poCS->getX(2), poCS->getY(2),
2125                 R_1, cx_1, cy_1, alpha0_1, alpha1_1, alpha2_1) &&
2126             OGRGeometryFactory::GetCurveParameters(
2127                 poCS->getX(2), poCS->getY(2),
2128                 poCS->getX(3), poCS->getY(3),
2129                 poCS->getX(4), poCS->getY(4),
2130                 R_2, cx_2, cy_2, alpha0_2, alpha1_2, alpha2_2) &&
2131             AlmostEqual(R_1,R_2) &&
2132             AlmostEqual(cx_1,cx_2) &&
2133             AlmostEqual(cy_1,cy_2) &&
2134             (alpha2_1 - alpha0_1) * (alpha2_2 - alpha0_2) > 0 )
2135         {
2136             cx = cx_1;
2137             cy = cy_1;
2138             R = R_1;
2139             return true;
2140         }
2141     }
2142     return false;
2143 }
2144 
2145 /************************************************************************/
2146 /*                       CreateGraphicsElement()                        */
2147 /*                                                                      */
2148 /*      Create an element or element group from a given geometry and    */
2149 /*      the given feature.  This method recurses to handle              */
2150 /*      collections as essentially independent features.                */
2151 /************************************************************************/
2152 
CreateGraphicsElement(OGRFeature * poFeature,OGRGeometry * poGeom)2153 OdDgGraphicsElementPtr OGRDGNV8Layer::CreateGraphicsElement(
2154                                              OGRFeature *poFeature,
2155                                              OGRGeometry *poGeom)
2156 
2157 {
2158     const OGRwkbGeometryType eType = poGeom->getGeometryType();
2159     const OGRwkbGeometryType eFType = wkbFlatten(eType);
2160 
2161     const int nType = poFeature->GetFieldAsInteger( "Type" );
2162 
2163     OdDgGraphicsElementPtr element;
2164 
2165     if( eFType == wkbPoint )
2166     {
2167         OGRPoint *poPoint = poGeom->toPoint();
2168         const char *pszText = poFeature->GetFieldAsString("Text");
2169         const char *pszStyle = poFeature->GetStyleString();
2170 
2171         if( (pszText == nullptr || pszText[0] == 0)
2172             && (pszStyle == nullptr || strstr(pszStyle,"LABEL") == nullptr) )
2173         {
2174             if( OGR_GT_HasZ(eType) )
2175             {
2176                 OdDgLine3dPtr line = OdDgLine3d::createObject();
2177                 element = line;
2178                 OdGePoint3d point;
2179                 point.x = poPoint->getX();
2180                 point.y = poPoint->getY();
2181                 point.z = poPoint->getZ();
2182                 line->setStartPoint(point);
2183                 line->setEndPoint(point);
2184             }
2185             else
2186             {
2187                 OdDgLine2dPtr line = OdDgLine2d::createObject();
2188                 element = line;
2189                 OdGePoint2d point;
2190                 point.x = poPoint->getX();
2191                 point.y = poPoint->getY();
2192                 line->setStartPoint(point);
2193                 line->setEndPoint(point);
2194             }
2195         }
2196         else
2197         {
2198             element = TranslateLabel( poFeature, poPoint );
2199         }
2200     }
2201     else if( eFType == wkbLineString )
2202     {
2203         OGRLineString* poLS = poGeom->toLineString();
2204         if( poLS->getNumPoints() == 2 &&
2205             (nType == 0 || nType == OdDgElement::kTypeLine) )
2206         {
2207             if( OGR_GT_HasZ(eType) )
2208             {
2209                 OdDgLine3dPtr line = OdDgLine3d::createObject();
2210                 element = line;
2211                 OGRPoint ogrPoint;
2212                 OdGePoint3d point;
2213                 poLS->getPoint(0, &ogrPoint);
2214                 point.x = ogrPoint.getX();
2215                 point.y = ogrPoint.getY();
2216                 point.z = ogrPoint.getZ();
2217                 line->setStartPoint(point);
2218                 poLS->getPoint(1, &ogrPoint);
2219                 point.x = ogrPoint.getX();
2220                 point.y = ogrPoint.getY();
2221                 point.z = ogrPoint.getZ();
2222                 line->setEndPoint(point);
2223             }
2224             else
2225             {
2226                 OdDgLine2dPtr line = OdDgLine2d::createObject();
2227                 element = line;
2228                 OGRPoint ogrPoint;
2229                 OdGePoint2d point;
2230                 poLS->getPoint(0, &ogrPoint);
2231                 point.x = ogrPoint.getX();
2232                 point.y = ogrPoint.getY();
2233                 line->setStartPoint(point);
2234                 poLS->getPoint(1, &ogrPoint);
2235                 point.x = ogrPoint.getX();
2236                 point.y = ogrPoint.getY();
2237                 line->setEndPoint(point);
2238             }
2239         }
2240         else
2241         {
2242             if( OGR_GT_HasZ(eType) )
2243             {
2244                 OdDgLineString3dPtr line = OdDgLineString3d::createObject();
2245                 element = line;
2246                 for( int i = 0; i < poLS->getNumPoints(); i++ )
2247                 {
2248                     OGRPoint ogrPoint;
2249                     OdGePoint3d point;
2250                     poLS->getPoint(i, &ogrPoint);
2251                     point.x = ogrPoint.getX();
2252                     point.y = ogrPoint.getY();
2253                     point.z = ogrPoint.getZ();
2254                     line->addVertex(point);
2255                 }
2256             }
2257             else
2258             {
2259                 OdDgLineString2dPtr line = OdDgLineString2d::createObject();
2260                 element = line;
2261                 for( int i = 0; i < poLS->getNumPoints(); i++ )
2262                 {
2263                     OGRPoint ogrPoint;
2264                     OdGePoint2d point;
2265                     poLS->getPoint(i, &ogrPoint);
2266                     point.x = ogrPoint.getX();
2267                     point.y = ogrPoint.getY();
2268                     line->addVertex(point);
2269                 }
2270             }
2271         }
2272     }
2273     else if( eFType == wkbCircularString )
2274     {
2275         OGRCircularString* poCS = poGeom->toCircularString();
2276         double R, cx, cy;
2277         if( IsFullCircle(poCS, cx, cy, R) && !OGR_GT_HasZ(eType) )
2278         {
2279             OdDgEllipse2dPtr ellipse = OdDgEllipse2d::createObject();
2280             element = ellipse;
2281             ellipse->setPrimaryAxis(R);
2282             ellipse->setSecondaryAxis(R);
2283             OdGePoint2d point;
2284             point.x = cx;
2285             point.y = cy;
2286             ellipse->setOrigin(point);
2287         }
2288         else if( poCS->getNumPoints() == 3 && !OGR_GT_HasZ(eType) )
2289         {
2290             double alpha0, alpha1, alpha2;
2291             if( OGRGeometryFactory::GetCurveParameters(
2292                     poCS->getX(0),
2293                     poCS->getY(0),
2294                     poCS->getX(1),
2295                     poCS->getY(1),
2296                     poCS->getX(2),
2297                     poCS->getY(2),
2298                     R, cx, cy, alpha0, alpha1, alpha2) )
2299             {
2300                 OdDgArc2dPtr arc = OdDgArc2d::createObject();
2301                 element = arc;
2302                 arc->setPrimaryAxis(R);
2303                 arc->setSecondaryAxis(R);
2304                 OdGePoint2d point;
2305                 point.x = cx;
2306                 point.y = cy;
2307                 arc->setOrigin(point);
2308                 arc->setStartAngle(alpha0); // already in radians
2309                 arc->setSweepAngle(alpha2 - alpha0);
2310             }
2311         }
2312         else if( !OGR_GT_HasZ(eType) )
2313         {
2314             OdDgComplexCurvePtr complexCurve =
2315                             OdDgComplexString::createObject();
2316             element = complexCurve;
2317             AddToComplexCurve(poFeature, poCS, complexCurve);
2318         }
2319 
2320         if( element.isNull() )
2321         {
2322             OGRGeometry* poLS =
2323                 OGRGeometryFactory::forceToLineString( poGeom->clone() );
2324             element = CreateGraphicsElement(poFeature, poLS);
2325             delete poLS;
2326             return element;
2327         }
2328     }
2329     else if( eFType == wkbCompoundCurve )
2330     {
2331         OGRCompoundCurve* poCC = poGeom->toCompoundCurve();
2332         OdDgComplexCurvePtr complexCurve = OdDgComplexString::createObject();
2333         element = complexCurve;
2334         AddToComplexCurve( poFeature, poCC, complexCurve );
2335     }
2336     else if( eFType == wkbCurvePolygon || eFType == wkbPolygon )
2337     {
2338         OGRCurvePolygon* poPoly = poGeom->toCurvePolygon();
2339         if( poPoly->getNumInteriorRings() == 0 &&
2340             poPoly->getExteriorRingCurve() == nullptr )
2341         {
2342             if( OGR_GT_HasZ(eType) )
2343             {
2344                 element = OdDgShape3d::createObject();
2345             }
2346             else
2347             {
2348                 element = OdDgShape2d::createObject();
2349             }
2350         }
2351         else if( poPoly->getNumInteriorRings() == 0 )
2352         {
2353             element = CreateShape(poFeature, poPoly->getExteriorRingCurve());
2354         }
2355         else
2356         {
2357             if( OGR_GT_HasZ(eType) )
2358             {
2359                 OdDgCellHeader3dPtr pCell = OdDgCellHeader3d::createObject();
2360                 element = pCell;
2361                 for( int iRing = -1;
2362                          iRing < poPoly->getNumInteriorRings(); iRing++ )
2363                 {
2364                     OGRCurve* poCurve = (iRing < 0 ) ?
2365                             poPoly->getExteriorRingCurve() :
2366                            poPoly->getInteriorRingCurve(iRing);
2367 
2368                     OdDgGraphicsElementPtr shape = CreateShape(
2369                         poFeature, poCurve, iRing >= 0);
2370                     AttachCommonAttributes(poFeature, shape);
2371                     pCell->add(shape);
2372                 }
2373             }
2374             else
2375             {
2376                 OdDgCellHeader2dPtr pCell = OdDgCellHeader2d::createObject();
2377                 element = pCell;
2378                 for( int iRing = -1;
2379                          iRing < poPoly->getNumInteriorRings(); iRing++ )
2380                 {
2381                     OGRCurve* poCurve = (iRing < 0 ) ?
2382                             poPoly->getExteriorRingCurve() :
2383                            poPoly->getInteriorRingCurve(iRing);
2384 
2385                     OdDgGraphicsElementPtr shape = CreateShape(
2386                         poFeature, poCurve, iRing >= 0);
2387                     AttachCommonAttributes(poFeature, shape);
2388                     pCell->add(shape);
2389                 }
2390             }
2391             AttachFillLinkage( poFeature, element );
2392         }
2393     }
2394     else if( OGR_GT_IsSubClassOf( eFType, wkbGeometryCollection ) )
2395     {
2396         OGRGeometryCollection* poGC = poGeom->toGeometryCollection();
2397         OdDgCellHeader2dPtr pCell = OdDgCellHeader2d::createObject();
2398         element = pCell;
2399         if( !pCell.isNull() )
2400         {
2401             for( auto&& poMember: poGC )
2402             {
2403                 pCell->add(CreateGraphicsElement( poFeature, poMember ));
2404             }
2405         }
2406     }
2407     else
2408     {
2409         CPLError( CE_Failure, CPLE_AppDefined,
2410                   "Unsupported geometry type (%s) for DGN.",
2411                   OGRGeometryTypeToName( eType ) );
2412     }
2413 
2414     if( !element.isNull() )
2415         AttachCommonAttributes(poFeature, element);
2416 
2417     return element;
2418 }
2419