LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dgn - ogrdgnlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 411 586 70.1 %
Date: 2025-07-08 21:33:46 Functions: 14 17 82.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGRDGNLayer class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2000, Frank Warmerdam (warmerdam@pobox.com)
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_dgn.h"
      14             : #include "cpl_conv.h"
      15             : #include "ogr_featurestyle.h"
      16             : #include "ogr_api.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <cmath>
      20             : #include <list>
      21             : 
      22             : /************************************************************************/
      23             : /*                           OGRDGNLayer()                              */
      24             : /************************************************************************/
      25             : 
      26          76 : OGRDGNLayer::OGRDGNLayer(OGRDGNDataSource *poDS, const char *pszName,
      27          76 :                          DGNHandle hDGNIn, int bUpdateIn)
      28          76 :     : m_poDS(poDS), poFeatureDefn(new OGRFeatureDefn(pszName)), iNextShapeId(0),
      29          76 :       hDGN(hDGNIn), bUpdate(bUpdateIn)
      30             : {
      31             : 
      32             :     /* -------------------------------------------------------------------- */
      33             :     /*      Work out what link format we are using.                         */
      34             :     /* -------------------------------------------------------------------- */
      35             :     OGRFieldType eLinkFieldType;
      36             : 
      37          76 :     pszLinkFormat =
      38          76 :         const_cast<char *>(CPLGetConfigOption("DGN_LINK_FORMAT", "FIRST"));
      39             : 
      40          76 :     if (EQUAL(pszLinkFormat, "FIRST"))
      41          76 :         eLinkFieldType = OFTInteger;
      42           0 :     else if (EQUAL(pszLinkFormat, "LIST"))
      43           0 :         eLinkFieldType = OFTIntegerList;
      44           0 :     else if (EQUAL(pszLinkFormat, "STRING"))
      45           0 :         eLinkFieldType = OFTString;
      46             :     else
      47             :     {
      48           0 :         CPLError(CE_Warning, CPLE_AppDefined,
      49             :                  "DGN_LINK_FORMAT=%s, but only FIRST, LIST or STRING "
      50             :                  "supported.",
      51             :                  pszLinkFormat);
      52           0 :         pszLinkFormat = const_cast<char *>("FIRST");
      53           0 :         eLinkFieldType = OFTInteger;
      54             :     }
      55          76 :     pszLinkFormat = CPLStrdup(pszLinkFormat);
      56             : 
      57             :     /* -------------------------------------------------------------------- */
      58             :     /*      Create the feature definition.                                  */
      59             :     /* -------------------------------------------------------------------- */
      60          76 :     SetDescription(poFeatureDefn->GetName());
      61          76 :     poFeatureDefn->Reference();
      62             : 
      63          76 :     OGRFieldDefn oField("", OFTInteger);
      64             : 
      65             :     /* -------------------------------------------------------------------- */
      66             :     /*      Element type                                                    */
      67             :     /* -------------------------------------------------------------------- */
      68          76 :     oField.SetName("Type");
      69          76 :     oField.SetType(OFTInteger);
      70          76 :     oField.SetWidth(2);
      71          76 :     oField.SetPrecision(0);
      72          76 :     poFeatureDefn->AddFieldDefn(&oField);
      73             : 
      74             :     /* -------------------------------------------------------------------- */
      75             :     /*      Level number.                                                   */
      76             :     /* -------------------------------------------------------------------- */
      77          76 :     oField.SetName("Level");
      78          76 :     oField.SetType(OFTInteger);
      79          76 :     oField.SetWidth(2);
      80          76 :     oField.SetPrecision(0);
      81          76 :     poFeatureDefn->AddFieldDefn(&oField);
      82             : 
      83             :     /* -------------------------------------------------------------------- */
      84             :     /*      graphic group                                                   */
      85             :     /* -------------------------------------------------------------------- */
      86          76 :     oField.SetName("GraphicGroup");
      87          76 :     oField.SetType(OFTInteger);
      88          76 :     oField.SetWidth(4);
      89          76 :     oField.SetPrecision(0);
      90          76 :     poFeatureDefn->AddFieldDefn(&oField);
      91             : 
      92             :     /* -------------------------------------------------------------------- */
      93             :     /*      ColorIndex                                                      */
      94             :     /* -------------------------------------------------------------------- */
      95          76 :     oField.SetName("ColorIndex");
      96          76 :     oField.SetType(OFTInteger);
      97          76 :     oField.SetWidth(3);
      98          76 :     oField.SetPrecision(0);
      99          76 :     poFeatureDefn->AddFieldDefn(&oField);
     100             : 
     101             :     /* -------------------------------------------------------------------- */
     102             :     /*      Weight                                                          */
     103             :     /* -------------------------------------------------------------------- */
     104          76 :     oField.SetName("Weight");
     105          76 :     oField.SetType(OFTInteger);
     106          76 :     oField.SetWidth(2);
     107          76 :     oField.SetPrecision(0);
     108          76 :     poFeatureDefn->AddFieldDefn(&oField);
     109             : 
     110             :     /* -------------------------------------------------------------------- */
     111             :     /*      Style                                                           */
     112             :     /* -------------------------------------------------------------------- */
     113          76 :     oField.SetName("Style");
     114          76 :     oField.SetType(OFTInteger);
     115          76 :     oField.SetWidth(1);
     116          76 :     oField.SetPrecision(0);
     117          76 :     poFeatureDefn->AddFieldDefn(&oField);
     118             : 
     119             :     /* -------------------------------------------------------------------- */
     120             :     /*      EntityNum                                                       */
     121             :     /* -------------------------------------------------------------------- */
     122          76 :     oField.SetName("EntityNum");
     123          76 :     oField.SetType(eLinkFieldType);
     124          76 :     oField.SetWidth(0);
     125          76 :     oField.SetPrecision(0);
     126          76 :     poFeatureDefn->AddFieldDefn(&oField);
     127             : 
     128             :     /* -------------------------------------------------------------------- */
     129             :     /*      MSLink                                                          */
     130             :     /* -------------------------------------------------------------------- */
     131          76 :     oField.SetName("MSLink");
     132          76 :     oField.SetType(eLinkFieldType);
     133          76 :     oField.SetWidth(0);
     134          76 :     oField.SetPrecision(0);
     135          76 :     poFeatureDefn->AddFieldDefn(&oField);
     136             : 
     137             :     /* -------------------------------------------------------------------- */
     138             :     /*      Text                                                            */
     139             :     /* -------------------------------------------------------------------- */
     140          76 :     oField.SetName("Text");
     141          76 :     oField.SetType(OFTString);
     142          76 :     oField.SetWidth(0);
     143          76 :     oField.SetPrecision(0);
     144          76 :     poFeatureDefn->AddFieldDefn(&oField);
     145             : 
     146             :     /* -------------------------------------------------------------------- */
     147             :     /*      ULink                                                           */
     148             :     /* -------------------------------------------------------------------- */
     149          76 :     oField.SetName("ULink");
     150          76 :     oField.SetType(OFTString);
     151          76 :     oField.SetSubType(OFSTJSON);
     152          76 :     oField.SetWidth(0);
     153          76 :     oField.SetPrecision(0);
     154          76 :     poFeatureDefn->AddFieldDefn(&oField);
     155             : 
     156             :     /* -------------------------------------------------------------------- */
     157             :     /*      Create template feature for evaluating simple expressions.      */
     158             :     /* -------------------------------------------------------------------- */
     159          76 :     poEvalFeature = new OGRFeature(poFeatureDefn);
     160             : 
     161             :     /* TODO: I am intending to keep track of simple attribute queries (ones
     162             :        using only FID, Type and Level and short circuiting their operation
     163             :        based on the index.  However, there are some complexities with
     164             :        complex elements, and spatial queries that have caused me to put it
     165             :        off for now.
     166             :     */
     167          76 : }
     168             : 
     169             : /************************************************************************/
     170             : /*                           ~OGRDGNLayer()                             */
     171             : /************************************************************************/
     172             : 
     173         152 : OGRDGNLayer::~OGRDGNLayer()
     174             : 
     175             : {
     176          76 :     if (m_nFeaturesRead > 0)
     177             :     {
     178          52 :         CPLDebug("Mem", "%d features read on layer '%s'.",
     179          26 :                  static_cast<int>(m_nFeaturesRead), poFeatureDefn->GetName());
     180             :     }
     181             : 
     182          76 :     delete poEvalFeature;
     183             : 
     184          76 :     poFeatureDefn->Release();
     185             : 
     186          76 :     CPLFree(pszLinkFormat);
     187         152 : }
     188             : 
     189             : /************************************************************************/
     190             : /*                         ISetSpatialFilter()                          */
     191             : /************************************************************************/
     192             : 
     193           4 : OGRErr OGRDGNLayer::ISetSpatialFilter(int, const OGRGeometry *poGeomIn)
     194             : 
     195             : {
     196           4 :     if (!InstallFilter(poGeomIn))
     197           2 :         return OGRERR_NONE;
     198             : 
     199           2 :     if (m_poFilterGeom != nullptr)
     200             :     {
     201           1 :         DGNSetSpatialFilter(hDGN, m_sFilterEnvelope.MinX,
     202             :                             m_sFilterEnvelope.MinY, m_sFilterEnvelope.MaxX,
     203             :                             m_sFilterEnvelope.MaxY);
     204             :     }
     205             :     else
     206             :     {
     207           1 :         DGNSetSpatialFilter(hDGN, 0.0, 0.0, 0.0, 0.0);
     208             :     }
     209             : 
     210           2 :     ResetReading();
     211           2 :     return OGRERR_NONE;
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                            ResetReading()                            */
     216             : /************************************************************************/
     217             : 
     218          24 : void OGRDGNLayer::ResetReading()
     219             : 
     220             : {
     221          24 :     iNextShapeId = 0;
     222          24 :     DGNRewind(hDGN);
     223          24 : }
     224             : 
     225             : /************************************************************************/
     226             : /*                             GetFeature()                             */
     227             : /************************************************************************/
     228             : 
     229           0 : OGRFeature *OGRDGNLayer::GetFeature(GIntBig nFeatureId)
     230             : 
     231             : {
     232           0 :     if (nFeatureId > INT_MAX ||
     233           0 :         !DGNGotoElement(hDGN, static_cast<int>(nFeatureId)))
     234           0 :         return nullptr;
     235             : 
     236             :     // We should likely clear the spatial search region as it affects
     237             :     // DGNReadElement(), but I will defer that for now.
     238             : 
     239           0 :     DGNElemCore *psElement = DGNReadElement(hDGN);
     240           0 :     OGRFeature *poFeature = ElementToFeature(psElement, 0);
     241           0 :     DGNFreeElement(hDGN, psElement);
     242             : 
     243           0 :     if (poFeature == nullptr)
     244           0 :         return nullptr;
     245             : 
     246           0 :     if (poFeature->GetFID() != nFeatureId)
     247             :     {
     248           0 :         delete poFeature;
     249           0 :         return nullptr;
     250             :     }
     251             : 
     252           0 :     return poFeature;
     253             : }
     254             : 
     255             : /************************************************************************/
     256             : /*                           ConsiderBrush()                            */
     257             : /*                                                                      */
     258             : /*      Method to set the style for a polygon, including a brush if     */
     259             : /*      appropriate.                                                    */
     260             : /************************************************************************/
     261             : 
     262          10 : void OGRDGNLayer::ConsiderBrush(DGNElemCore *psElement, const char *pszPen,
     263             :                                 OGRFeature *poFeature)
     264             : 
     265             : {
     266          10 :     int nFillColor = 0;
     267          10 :     int gv_red = 0;
     268          10 :     int gv_green = 0;
     269          10 :     int gv_blue = 0;
     270             : 
     271          14 :     if (DGNGetShapeFillInfo(hDGN, psElement, &nFillColor) &&
     272           4 :         DGNLookupColor(hDGN, nFillColor, &gv_red, &gv_green, &gv_blue))
     273             :     {
     274           8 :         CPLString osFullStyle;
     275           4 :         osFullStyle.Printf("BRUSH(fc:#%02x%02x%02x,id:\"ogr-brush-0\")", gv_red,
     276           4 :                            gv_green, gv_blue);
     277             : 
     278           4 :         if (nFillColor != psElement->color)
     279             :         {
     280           0 :             osFullStyle += ';';
     281           0 :             osFullStyle += pszPen;
     282             :         }
     283           4 :         poFeature->SetStyleString(osFullStyle.c_str());
     284             :     }
     285             :     else
     286           6 :         poFeature->SetStyleString(pszPen);
     287          10 : }
     288             : 
     289             : /************************************************************************/
     290             : /*                          ElementToFeature()                          */
     291             : /************************************************************************/
     292             : 
     293         284 : OGRFeature *OGRDGNLayer::ElementToFeature(DGNElemCore *psElement, int nRecLevel)
     294             : 
     295             : {
     296         284 :     OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
     297             : 
     298         284 :     poFeature->SetFID(psElement->element_id);
     299         284 :     poFeature->SetField("Type", psElement->type);
     300         284 :     poFeature->SetField("Level", psElement->level);
     301         284 :     poFeature->SetField("GraphicGroup", psElement->graphic_group);
     302         284 :     poFeature->SetField("ColorIndex", psElement->color);
     303         284 :     poFeature->SetField("Weight", psElement->weight);
     304         284 :     poFeature->SetField("Style", psElement->style);
     305             : 
     306         284 :     m_nFeaturesRead++;
     307             : 
     308             :     /* -------------------------------------------------------------------- */
     309             :     /*      Collect linkage information                                     */
     310             :     /* -------------------------------------------------------------------- */
     311         284 :     const int MAX_LINK = 100;
     312             : 
     313             :     int anEntityNum[MAX_LINK];
     314         284 :     anEntityNum[0] = 0;
     315             : 
     316             :     int anMSLink[MAX_LINK];
     317         284 :     anMSLink[0] = 0;
     318             : 
     319         284 :     CPLJSONObject uLinkData;
     320             : 
     321         284 :     int iLink = 0;
     322         284 :     int nLinkCount = 0;
     323         284 :     int uLinkCount = 0;
     324             : 
     325         284 :     int nLinkType = 0;
     326         284 :     int nLinkSize = 0;
     327             : 
     328             :     // coverity[tained_data]
     329             :     unsigned char *pabyData =
     330         284 :         DGNGetLinkage(hDGN, psElement, iLink, &nLinkType, anEntityNum + iLink,
     331         284 :                       anMSLink + iLink, &nLinkSize);
     332             : 
     333         310 :     while (pabyData)
     334             :     {
     335             :         CPLJSONArray previousValues =
     336          26 :             uLinkData.GetArray(std::to_string(nLinkType));
     337          26 :         if (!previousValues.IsValid())
     338             :         {
     339          26 :             uLinkData.Add(std::to_string(nLinkType), CPLJSONArray());
     340          26 :             previousValues = uLinkData.GetArray(std::to_string(nLinkType));
     341             :         }
     342          26 :         CPLJSONArray rawWords;
     343         230 :         for (int i = 0; i < nLinkSize - 1; i += 2)
     344             :         {
     345         204 :             rawWords.Add(
     346         204 :                 CPLSPrintf("0x%02x%02x", pabyData[i + 1], pabyData[i]));
     347             :         }
     348          26 :         CPLJSONObject theNewObject = CPLJSONObject();
     349          26 :         theNewObject.Add("size", nLinkSize);
     350          26 :         previousValues.Add(theNewObject);
     351          26 :         switch (nLinkType)
     352             :         {
     353           0 :             case 24721:  // OdDgDBLinkage::kOracle
     354             :             {
     355           0 :                 theNewObject.Add("raw", rawWords);
     356           0 :                 theNewObject.Add("type", "Oracle");
     357             :             }
     358           0 :             break;
     359           0 :             case 32047:  // OdDgDBLinkage::kODBC
     360             :             {
     361           0 :                 theNewObject.Add("raw", rawWords);
     362           0 :                 theNewObject.Add("type", "ODBC");
     363             :             }
     364           0 :             break;
     365           0 :             case 6549:  // 0x1995 Application ID by IPCC/Portugal
     366             :             {
     367           0 :                 theNewObject.Add("domain", CPLSPrintf("0x%02x", pabyData[5]));
     368           0 :                 theNewObject.Add("subdomain",
     369           0 :                                  CPLSPrintf("0x%02x", pabyData[4]));
     370           0 :                 theNewObject.Add("family", CPLSPrintf("0x%02x", pabyData[7]));
     371           0 :                 theNewObject.Add("object", CPLSPrintf("0x%02x", pabyData[6]));
     372           0 :                 theNewObject.Add("key", CPLSPrintf("%02x%02x%02x%02x",
     373           0 :                                                    pabyData[5], pabyData[4],
     374           0 :                                                    pabyData[7], pabyData[6]));
     375           0 :                 theNewObject.Add("type", "IPCC/Portugal");
     376             :             }
     377           0 :             break;
     378          26 :             default:
     379             :             {
     380          26 :                 theNewObject.Add("raw", rawWords);
     381          26 :                 theNewObject.Add("type", "unknown");
     382             :             }
     383          26 :             break;
     384             :         }
     385             : 
     386          26 :         uLinkCount++;
     387          26 :         iLink++;
     388             : 
     389          26 :         if (anEntityNum[nLinkCount] != 0 || anMSLink[nLinkCount] != 0)
     390             :         {
     391           0 :             nLinkCount++;
     392           0 :             if (nLinkCount == MAX_LINK)
     393             :             {
     394           0 :                 break;
     395             :             }
     396             :         }
     397             : 
     398          26 :         anEntityNum[nLinkCount] = 0;
     399          26 :         anMSLink[nLinkCount] = 0;
     400             : 
     401             :         // coverity[tained_data]
     402          26 :         pabyData = DGNGetLinkage(hDGN, psElement, iLink, &nLinkType,
     403          26 :                                  anEntityNum + nLinkCount,
     404          26 :                                  anMSLink + nLinkCount, &nLinkSize);
     405             :     }
     406             : 
     407             :     /* -------------------------------------------------------------------- */
     408             :     /*      Apply attribute linkage to feature.                             */
     409             :     /* -------------------------------------------------------------------- */
     410         284 :     if (uLinkCount > 0)
     411             :     {
     412          25 :         poFeature->SetField("ULink", uLinkData.ToString().c_str());
     413             :     }
     414         284 :     if (nLinkCount > 0)
     415             :     {
     416           0 :         if (EQUAL(pszLinkFormat, "FIRST"))
     417             :         {
     418           0 :             poFeature->SetField("EntityNum", anEntityNum[0]);
     419           0 :             poFeature->SetField("MSLink", anMSLink[0]);
     420             :         }
     421           0 :         else if (EQUAL(pszLinkFormat, "LIST"))
     422             :         {
     423           0 :             poFeature->SetField("EntityNum", nLinkCount, anEntityNum);
     424           0 :             poFeature->SetField("MSLink", nLinkCount, anMSLink);
     425             :         }
     426           0 :         else if (EQUAL(pszLinkFormat, "STRING"))
     427             :         {
     428             :             char szEntityList[MAX_LINK * 9];
     429             :             char szMSLinkList[MAX_LINK * 9];
     430           0 :             int nEntityLen = 0;
     431           0 :             int nMSLinkLen = 0;
     432             : 
     433           0 :             for (iLink = 0; iLink < nLinkCount; iLink++)
     434             :             {
     435           0 :                 if (iLink != 0)
     436             :                 {
     437           0 :                     szEntityList[nEntityLen++] = ',';
     438           0 :                     szMSLinkList[nMSLinkLen++] = ',';
     439             :                 }
     440             : 
     441           0 :                 snprintf(szEntityList + nEntityLen,
     442           0 :                          sizeof(szEntityList) - nEntityLen, "%d",
     443             :                          anEntityNum[iLink]);
     444           0 :                 snprintf(szMSLinkList + nMSLinkLen,
     445           0 :                          sizeof(szMSLinkList) - nMSLinkLen, "%d",
     446             :                          anMSLink[iLink]);
     447             : 
     448           0 :                 nEntityLen +=
     449           0 :                     static_cast<int>(strlen(szEntityList + nEntityLen));
     450           0 :                 nMSLinkLen +=
     451           0 :                     static_cast<int>(strlen(szMSLinkList + nMSLinkLen));
     452             :             }
     453             : 
     454           0 :             poFeature->SetField("EntityNum", szEntityList);
     455           0 :             poFeature->SetField("MSLink", szMSLinkList);
     456             :         }
     457             :     }
     458             : 
     459             :     /* -------------------------------------------------------------------- */
     460             :     /*      Lookup color.                                                   */
     461             :     /* -------------------------------------------------------------------- */
     462         284 :     int gv_red = 0;
     463         284 :     int gv_green = 0;
     464         284 :     int gv_blue = 0;
     465             : 
     466         284 :     char szFSColor[128] = {};
     467         284 :     szFSColor[0] = '\0';
     468         284 :     if (DGNLookupColor(hDGN, psElement->color, &gv_red, &gv_green, &gv_blue))
     469             :     {
     470             :         char gv_color[128];
     471         284 :         CPLsnprintf(gv_color, sizeof(gv_color), "%f %f %f 1.0", gv_red / 255.0,
     472             :                     gv_green / 255.0, gv_blue / 255.0);
     473             : 
     474         284 :         snprintf(szFSColor, sizeof(szFSColor), "c:#%02x%02x%02x", gv_red,
     475             :                  gv_green, gv_blue);
     476             :     }
     477             : 
     478             :     /* -------------------------------------------------------------------- */
     479             :     /*      Generate corresponding PEN style.                               */
     480             :     /* -------------------------------------------------------------------- */
     481             :     char szPen[256];
     482         284 :     szPen[0] = '\0';
     483             : 
     484         284 :     if (psElement->style == DGNS_SOLID)
     485         258 :         snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-0\"");
     486          26 :     else if (psElement->style == DGNS_DOTTED)
     487          26 :         snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-5\"");
     488           0 :     else if (psElement->style == DGNS_MEDIUM_DASH)
     489           0 :         snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-2\"");
     490           0 :     else if (psElement->style == DGNS_LONG_DASH)
     491           0 :         snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-4\"");
     492           0 :     else if (psElement->style == DGNS_DOT_DASH)
     493           0 :         snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-6\"");
     494           0 :     else if (psElement->style == DGNS_SHORT_DASH)
     495           0 :         snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-3\"");
     496           0 :     else if (psElement->style == DGNS_DASH_DOUBLE_DOT)
     497           0 :         snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-7\"");
     498           0 :     else if (psElement->style == DGNS_LONG_DASH_SHORT_DASH)
     499           0 :         snprintf(szPen, sizeof(szPen), "PEN(p:\"10px 5px 4px 5px\"");
     500             :     else
     501           0 :         snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-0\"");
     502             : 
     503         284 :     if (strlen(szFSColor) > 0)
     504         284 :         snprintf(szPen + strlen(szPen), sizeof(szPen) - strlen(szPen), ",%s",
     505             :                  szFSColor);
     506             : 
     507         284 :     if (psElement->weight > 1)
     508           0 :         snprintf(szPen + strlen(szPen), sizeof(szPen) - strlen(szPen),
     509             :                  ",w:%dpx", psElement->weight);
     510             : 
     511         284 :     strcat(szPen, ")");
     512             : 
     513         284 :     switch (psElement->stype)
     514             :     {
     515          26 :         case DGNST_MULTIPOINT:
     516          26 :             if (psElement->type == DGNT_SHAPE)
     517             :             {
     518           9 :                 OGRLinearRing *poLine = new OGRLinearRing();
     519           9 :                 DGNElemMultiPoint *psEMP =
     520             :                     reinterpret_cast<DGNElemMultiPoint *>(psElement);
     521             : 
     522           9 :                 poLine->setNumPoints(psEMP->num_vertices);
     523          54 :                 for (int i = 0; i < psEMP->num_vertices; i++)
     524             :                 {
     525          45 :                     poLine->setPoint(i, psEMP->vertices[i].x,
     526             :                                      psEMP->vertices[i].y,
     527             :                                      psEMP->vertices[i].z);
     528             :                 }
     529             : 
     530           9 :                 OGRPolygon *poPolygon = new OGRPolygon();
     531           9 :                 poPolygon->addRingDirectly(poLine);
     532             : 
     533           9 :                 poFeature->SetGeometryDirectly(poPolygon);
     534             : 
     535           9 :                 ConsiderBrush(psElement, szPen, poFeature);
     536             :             }
     537          17 :             else if (psElement->type == DGNT_CURVE)
     538             :             {
     539           0 :                 OGRLineString *poLine = new OGRLineString();
     540           0 :                 DGNElemMultiPoint *psEMP =
     541             :                     reinterpret_cast<DGNElemMultiPoint *>(psElement);
     542           0 :                 const int nPoints = 5 * psEMP->num_vertices;
     543             :                 DGNPoint *pasPoints = static_cast<DGNPoint *>(
     544           0 :                     CPLMalloc(sizeof(DGNPoint) * nPoints));
     545             : 
     546           0 :                 DGNStrokeCurve(hDGN, psEMP, nPoints, pasPoints);
     547             : 
     548           0 :                 poLine->setNumPoints(nPoints);
     549           0 :                 for (int i = 0; i < nPoints; i++)
     550             :                 {
     551           0 :                     poLine->setPoint(i, pasPoints[i].x, pasPoints[i].y,
     552           0 :                                      pasPoints[i].z);
     553             :                 }
     554             : 
     555           0 :                 poFeature->SetGeometryDirectly(poLine);
     556           0 :                 CPLFree(pasPoints);
     557             : 
     558           0 :                 poFeature->SetStyleString(szPen);
     559             :             }
     560             :             else
     561             :             {
     562          17 :                 DGNElemMultiPoint *psEMP =
     563             :                     reinterpret_cast<DGNElemMultiPoint *>(psElement);
     564             : 
     565          17 :                 if (psEMP->num_vertices > 0)
     566             :                 {
     567          17 :                     OGRLineString *poLine = new OGRLineString();
     568          17 :                     poLine->setNumPoints(psEMP->num_vertices);
     569         121 :                     for (int i = 0; i < psEMP->num_vertices; i++)
     570             :                     {
     571         104 :                         poLine->setPoint(i, psEMP->vertices[i].x,
     572             :                                          psEMP->vertices[i].y,
     573             :                                          psEMP->vertices[i].z);
     574             :                     }
     575             : 
     576          17 :                     poFeature->SetGeometryDirectly(poLine);
     577             :                 }
     578             : 
     579          17 :                 poFeature->SetStyleString(szPen);
     580             :             }
     581          26 :             break;
     582             : 
     583           6 :         case DGNST_ARC:
     584             :         {
     585           6 :             DGNElemArc *psArc = reinterpret_cast<DGNElemArc *>(psElement);
     586             :             int nPoints = static_cast<int>(
     587           6 :                 std::max(1.0, std::abs(psArc->sweepang) / 5.0) + 1.0);
     588           6 :             if (nPoints > 90)
     589           0 :                 nPoints = 90;
     590             : 
     591           6 :             DGNPoint asPoints[90] = {};
     592           6 :             DGNStrokeArc(hDGN, psArc, nPoints, asPoints);
     593             : 
     594           6 :             OGRLineString *poLine = new OGRLineString();
     595           6 :             poLine->setNumPoints(nPoints);
     596         444 :             for (int i = 0; i < nPoints; i++)
     597             :             {
     598         438 :                 poLine->setPoint(i, asPoints[i].x, asPoints[i].y,
     599             :                                  asPoints[i].z);
     600             :             }
     601             : 
     602           6 :             poFeature->SetGeometryDirectly(poLine);
     603           6 :             poFeature->SetStyleString(szPen);
     604             :         }
     605           6 :         break;
     606             : 
     607           9 :         case DGNST_TEXT:
     608             :         {
     609           9 :             OGRPoint *poPoint = new OGRPoint();
     610           9 :             DGNElemText *psText = reinterpret_cast<DGNElemText *>(psElement);
     611             : 
     612           9 :             poPoint->setX(psText->origin.x);
     613           9 :             poPoint->setY(psText->origin.y);
     614           9 :             poPoint->setZ(psText->origin.z);
     615             : 
     616           9 :             poFeature->SetGeometryDirectly(poPoint);
     617             : 
     618           9 :             const auto &osEncoding = m_poDS->GetEncoding();
     619          18 :             std::string osText;
     620           9 :             if (!osEncoding.empty() && osEncoding != CPL_ENC_UTF8)
     621             :             {
     622           2 :                 osText = CPLString(psText->string)
     623           1 :                              .Recode(osEncoding.c_str(), CPL_ENC_UTF8);
     624             :             }
     625             :             else
     626             :             {
     627           8 :                 osText = psText->string;
     628             :             }
     629             : 
     630           9 :             const size_t nOgrFSLen = osText.size() + 150;
     631           9 :             char *pszOgrFS = static_cast<char *>(CPLMalloc(nOgrFSLen));
     632             : 
     633             :             // setup the basic label.
     634           9 :             snprintf(pszOgrFS, nOgrFSLen, "LABEL(t:\"%s\"", osText.c_str());
     635             : 
     636             :             // set the color if we have it.
     637           9 :             if (strlen(szFSColor) > 0)
     638           9 :                 snprintf(pszOgrFS + strlen(pszOgrFS),
     639           9 :                          nOgrFSLen - strlen(pszOgrFS), ",%s", szFSColor);
     640             : 
     641             :             // Add the size info in ground units.
     642             :             // TODO: std::abs
     643           9 :             if (std::abs(psText->height_mult) >= 6.0)
     644           2 :                 CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
     645           2 :                             nOgrFSLen - strlen(pszOgrFS), ",s:%dg",
     646           2 :                             static_cast<int>(psText->height_mult));
     647           7 :             else if (std::abs(psText->height_mult) > 0.1)
     648           7 :                 CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
     649           7 :                             nOgrFSLen - strlen(pszOgrFS), ",s:%.3fg",
     650             :                             psText->height_mult);
     651             :             else
     652           0 :                 CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
     653           0 :                             nOgrFSLen - strlen(pszOgrFS), ",s:%.12fg",
     654             :                             psText->height_mult);
     655             : 
     656             :             // Add the font name. Name it MstnFont<FONTNUMBER> if not available
     657             :             // in the font list. #3392
     658             :             static const char *const papszFontList[] = {
     659             :                 "STANDARD",
     660             :                 "WORKING",
     661             :                 "FANCY",
     662             :                 "ENGINEERING",
     663             :                 "NEWZERO",
     664             :                 "STENCEL",  // 0-5
     665             :                 "USTN_FANCY",
     666             :                 "COMPRESSED",
     667             :                 "STENCEQ",
     668             :                 nullptr,
     669             :                 "hand",
     670             :                 "ARCH",  // 6-11
     671             :                 "ARCHB",
     672             :                 nullptr,
     673             :                 nullptr,
     674             :                 "IGES1001",
     675             :                 "IGES1002",
     676             :                 "IGES1003",  // 12-17
     677             :                 "CENTB",
     678             :                 "MICROS",
     679             :                 nullptr,
     680             :                 nullptr,
     681             :                 "ISOFRACTIONS",
     682             :                 "ITALICS",  // 18-23
     683             :                 "ISO30",
     684             :                 nullptr,
     685             :                 "GREEK",
     686             :                 "ISOREC",
     687             :                 "Isoeq",
     688             :                 nullptr,  // 24-29
     689             :                 "ISO_FONTLEFT",
     690             :                 "ISO_FONTRIGHT",
     691             :                 "INTL_ENGINEERING",
     692             :                 "INTL_WORKING",
     693             :                 "ISOITEQ",
     694             :                 nullptr,  // 30-35
     695             :                 "USTN FONT 26",
     696             :                 nullptr,
     697             :                 nullptr,
     698             :                 nullptr,
     699             :                 nullptr,
     700             :                 "ARCHITECTURAL",  // 36-41
     701             :                 "BLOCK_OUTLINE",
     702             :                 "LOW_RES_FILLED",
     703             :                 nullptr,
     704             :                 nullptr,
     705             :                 nullptr,
     706             :                 nullptr,  // 42-47
     707             :                 nullptr,
     708             :                 nullptr,
     709             :                 "UPPERCASE",
     710             :                 nullptr,
     711             :                 nullptr,
     712             :                 nullptr,  // 48-53
     713             :                 nullptr,
     714             :                 nullptr,
     715             :                 nullptr,
     716             :                 nullptr,
     717             :                 nullptr,
     718             :                 nullptr,  // 54-49
     719             :                 "FONT060",
     720             :                 "din",
     721             :                 "dinit",
     722             :                 "helvl",
     723             :                 "HELVLIT",
     724             :                 "helv",  // 60-65
     725             :                 "HELVIT",
     726             :                 "cent",
     727             :                 "CENTIT",
     728             :                 "SCRIPT",
     729             :                 nullptr,
     730             :                 nullptr,  // 66-71
     731             :                 nullptr,
     732             :                 nullptr,
     733             :                 nullptr,
     734             :                 nullptr,
     735             :                 "MICROQ",
     736             :                 "dotfont",  // 72-77
     737             :                 "DOTIT",
     738             :                 nullptr,
     739             :                 nullptr,
     740             :                 nullptr,
     741             :                 nullptr,
     742             :                 nullptr,  // 78-83
     743             :                 nullptr,
     744             :                 nullptr,
     745             :                 nullptr,
     746             :                 nullptr,
     747             :                 nullptr,
     748             :                 nullptr,  // 84-89
     749             :                 nullptr,
     750             :                 nullptr,
     751             :                 "FONT092",
     752             :                 nullptr,
     753             :                 "FONT094",
     754             :                 nullptr,  // 90-95
     755             :                 nullptr,
     756             :                 nullptr,
     757             :                 nullptr,
     758             :                 nullptr,
     759             :                 "ANSI_SYMBOLS",
     760             :                 "FEATURE_CONTROL_SYSMBOLS",  // 96-101
     761             :                 "SYMB_FAST",
     762             :                 nullptr,
     763             :                 nullptr,
     764             :                 "INTL_ISO",
     765             :                 "INTL_ISO_EQUAL",
     766             :                 "INTL_ISO_ITALIC",         // 102-107
     767             :                 "INTL_ISO_ITALIC_EQUAL"};  // 108
     768             : 
     769           9 :             if (psText->font_id <= 108 &&
     770           9 :                 papszFontList[psText->font_id] != nullptr)
     771             :             {
     772           9 :                 snprintf(pszOgrFS + strlen(pszOgrFS),
     773           9 :                          nOgrFSLen - strlen(pszOgrFS), ",f:%s",
     774           9 :                          papszFontList[psText->font_id]);
     775             :             }
     776             :             else
     777             :             {
     778           0 :                 snprintf(pszOgrFS + strlen(pszOgrFS),
     779           0 :                          nOgrFSLen - strlen(pszOgrFS), ",f:MstnFont%d",
     780             :                          psText->font_id);
     781             :             }
     782             : 
     783             :             // Add the angle, if not horizontal
     784           9 :             if (psText->rotation != 0.0)
     785           0 :                 snprintf(pszOgrFS + strlen(pszOgrFS),
     786           0 :                          nOgrFSLen - strlen(pszOgrFS), ",a:%d",
     787           0 :                          static_cast<int>(psText->rotation + 0.5));
     788             : 
     789           9 :             snprintf(pszOgrFS + strlen(pszOgrFS), nOgrFSLen - strlen(pszOgrFS),
     790             :                      ")");
     791             : 
     792           9 :             poFeature->SetStyleString(pszOgrFS);
     793           9 :             CPLFree(pszOgrFS);
     794             : 
     795           9 :             poFeature->SetField("Text", osText.c_str());
     796             :         }
     797           9 :         break;
     798             : 
     799           1 :         case DGNST_COMPLEX_HEADER:
     800             :         {
     801           1 :             DGNElemComplexHeader *psHdr =
     802             :                 reinterpret_cast<DGNElemComplexHeader *>(psElement);
     803           2 :             OGRMultiLineString oChildren;
     804             : 
     805             :             /* collect subsequent child geometries. */
     806             :             // we should disable the spatial filter ... add later.
     807           3 :             for (int iChild = 0; iChild < psHdr->numelems && nRecLevel < 20;
     808             :                  iChild++)
     809             :             {
     810           2 :                 OGRFeature *poChildFeature = nullptr;
     811           2 :                 DGNElemCore *psChildElement = DGNReadElement(hDGN);
     812             :                 // should verify complex bit set, not another header.
     813             : 
     814           2 :                 if (psChildElement != nullptr)
     815             :                 {
     816             :                     poChildFeature =
     817           2 :                         ElementToFeature(psChildElement, nRecLevel + 1);
     818           2 :                     DGNFreeElement(hDGN, psChildElement);
     819             :                 }
     820             : 
     821           4 :                 if (poChildFeature != nullptr &&
     822           2 :                     poChildFeature->GetGeometryRef() != nullptr)
     823             :                 {
     824           2 :                     OGRGeometry *poGeom = poChildFeature->GetGeometryRef();
     825           2 :                     if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
     826           2 :                         oChildren.addGeometry(poGeom);
     827             :                 }
     828             : 
     829           2 :                 if (poChildFeature != nullptr)
     830           2 :                     delete poChildFeature;
     831             :             }
     832             : 
     833             :             // Try to assemble into polygon geometry.
     834           1 :             OGRGeometry *poGeom = nullptr;
     835             : 
     836           1 :             if (psElement->type == DGNT_COMPLEX_SHAPE_HEADER)
     837           0 :                 poGeom = OGRGeometry::FromHandle(
     838             :                     OGRBuildPolygonFromEdges(OGRGeometry::ToHandle(&oChildren),
     839             :                                              TRUE, TRUE, 100000, nullptr));
     840             :             else
     841           1 :                 poGeom = oChildren.clone();
     842             : 
     843           1 :             if (poGeom != nullptr)
     844           1 :                 poFeature->SetGeometryDirectly(poGeom);
     845             : 
     846           1 :             ConsiderBrush(psElement, szPen, poFeature);
     847             :         }
     848           1 :         break;
     849             : 
     850         242 :         default:
     851         242 :             break;
     852             :     }
     853             : 
     854             :     /* -------------------------------------------------------------------- */
     855             :     /*      Fixup geometry dimension.                                       */
     856             :     /* -------------------------------------------------------------------- */
     857         284 :     if (poFeature->GetGeometryRef() != nullptr)
     858          84 :         poFeature->GetGeometryRef()->setCoordinateDimension(
     859          42 :             DGNGetDimension(hDGN));
     860             : 
     861         568 :     return poFeature;
     862             : }
     863             : 
     864             : /************************************************************************/
     865             : /*                           GetNextFeature()                           */
     866             : /************************************************************************/
     867             : 
     868          37 : OGRFeature *OGRDGNLayer::GetNextFeature()
     869             : 
     870             : {
     871          37 :     DGNGetElementIndex(hDGN, nullptr);
     872             : 
     873          37 :     DGNElemCore *psElement = nullptr;
     874         285 :     while ((psElement = DGNReadElement(hDGN)) != nullptr)
     875             :     {
     876         282 :         if (psElement->deleted)
     877             :         {
     878           0 :             DGNFreeElement(hDGN, psElement);
     879           0 :             continue;
     880             :         }
     881             : 
     882         282 :         OGRFeature *poFeature = ElementToFeature(psElement, 0);
     883         282 :         DGNFreeElement(hDGN, psElement);
     884             : 
     885         282 :         if (poFeature == nullptr)
     886           0 :             continue;
     887             : 
     888         282 :         if (poFeature->GetGeometryRef() == nullptr)
     889             :         {
     890         242 :             delete poFeature;
     891         242 :             continue;
     892             :         }
     893             : 
     894          74 :         if ((m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)) &&
     895          34 :             FilterGeometry(poFeature->GetGeometryRef()))
     896          34 :             return poFeature;
     897             : 
     898           6 :         delete poFeature;
     899             :     }
     900             : 
     901           3 :     return nullptr;
     902             : }
     903             : 
     904             : /************************************************************************/
     905             : /*                           TestCapability()                           */
     906             : /************************************************************************/
     907             : 
     908          72 : int OGRDGNLayer::TestCapability(const char *pszCap)
     909             : 
     910             : {
     911          72 :     if (EQUAL(pszCap, OLCRandomRead))
     912           0 :         return TRUE;
     913             : 
     914          72 :     else if (EQUAL(pszCap, OLCSequentialWrite))
     915          16 :         return bUpdate;
     916          56 :     else if (EQUAL(pszCap, OLCRandomWrite))
     917           0 :         return FALSE; /* maybe later? */
     918             : 
     919          56 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
     920           0 :         return m_poFilterGeom == nullptr || m_poAttrQuery == nullptr;
     921             : 
     922          56 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
     923           0 :         return FALSE;
     924             : 
     925          56 :     else if (EQUAL(pszCap, OLCFastGetExtent))
     926           0 :         return TRUE;
     927             : 
     928          56 :     else if (EQUAL(pszCap, OLCZGeometries))
     929           0 :         return TRUE;
     930             : 
     931          56 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
     932           4 :         return !m_poDS->GetEncoding().empty();
     933             : 
     934          52 :     return FALSE;
     935             : }
     936             : 
     937             : /************************************************************************/
     938             : /*                          GetFeatureCount()                           */
     939             : /************************************************************************/
     940             : 
     941           0 : GIntBig OGRDGNLayer::GetFeatureCount(int bForce)
     942             : 
     943             : {
     944             :     /* -------------------------------------------------------------------- */
     945             :     /*      If any odd conditions are in effect collect the information     */
     946             :     /*      normally.                                                       */
     947             :     /* -------------------------------------------------------------------- */
     948           0 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
     949           0 :         return OGRLayer::GetFeatureCount(bForce);
     950             : 
     951             :     /* -------------------------------------------------------------------- */
     952             :     /*      Otherwise scan the index.                                       */
     953             :     /* -------------------------------------------------------------------- */
     954           0 :     int nElementCount = 0;
     955           0 :     const DGNElementInfo *pasIndex = DGNGetElementIndex(hDGN, &nElementCount);
     956             : 
     957           0 :     int nFeatureCount = 0;
     958           0 :     bool bInComplexShape = false;
     959             : 
     960           0 :     for (int i = 0; i < nElementCount; i++)
     961             :     {
     962           0 :         if (pasIndex[i].flags & DGNEIF_DELETED)
     963           0 :             continue;
     964             : 
     965           0 :         switch (pasIndex[i].stype)
     966             :         {
     967           0 :             case DGNST_MULTIPOINT:
     968             :             case DGNST_ARC:
     969             :             case DGNST_TEXT:
     970           0 :                 if (!(pasIndex[i].flags & DGNEIF_COMPLEX) || !bInComplexShape)
     971             :                 {
     972           0 :                     nFeatureCount++;
     973           0 :                     bInComplexShape = false;
     974             :                 }
     975           0 :                 break;
     976             : 
     977           0 :             case DGNST_COMPLEX_HEADER:
     978           0 :                 nFeatureCount++;
     979           0 :                 bInComplexShape = true;
     980           0 :                 break;
     981             : 
     982           0 :             default:
     983           0 :                 break;
     984             :         }
     985             :     }
     986             : 
     987           0 :     return nFeatureCount;
     988             : }
     989             : 
     990             : /************************************************************************/
     991             : /*                            IGetExtent()                              */
     992             : /************************************************************************/
     993             : 
     994           0 : OGRErr OGRDGNLayer::IGetExtent(int /* iGeomField */, OGREnvelope *psExtent,
     995             :                                bool /* bForce */)
     996             : {
     997             :     double adfExtents[6];
     998             : 
     999           0 :     if (!DGNGetExtents(hDGN, adfExtents))
    1000           0 :         return OGRERR_FAILURE;
    1001             : 
    1002           0 :     psExtent->MinX = adfExtents[0];
    1003           0 :     psExtent->MinY = adfExtents[1];
    1004           0 :     psExtent->MaxX = adfExtents[3];
    1005           0 :     psExtent->MaxY = adfExtents[4];
    1006             : 
    1007           0 :     return OGRERR_NONE;
    1008             : }
    1009             : 
    1010             : /************************************************************************/
    1011             : /*                      LineStringToElementGroup()                      */
    1012             : /*                                                                      */
    1013             : /*      Convert an OGR line string to one or more DGN elements.  If     */
    1014             : /*      the input is too long for a single element (more than 38        */
    1015             : /*      points) we split it into multiple LINE_STRING elements, and     */
    1016             : /*      prefix with a complex group header element.                     */
    1017             : /*                                                                      */
    1018             : /*      This method can create handle creating shapes, or line          */
    1019             : /*      strings for the aggregate object, but the components of a       */
    1020             : /*      complex shape group are always line strings.                    */
    1021             : /************************************************************************/
    1022             : 
    1023             : constexpr int MAX_ELEM_POINTS = 38;
    1024             : 
    1025          29 : DGNElemCore **OGRDGNLayer::LineStringToElementGroup(const OGRLineString *poLS,
    1026             :                                                     int nGroupType)
    1027             : 
    1028             : {
    1029          29 :     const int nTotalPoints = poLS->getNumPoints();
    1030          29 :     int iGeom = 0;
    1031             :     DGNElemCore **papsGroup = static_cast<DGNElemCore **>(
    1032          29 :         CPLCalloc(sizeof(void *), (nTotalPoints / (MAX_ELEM_POINTS - 1)) + 3));
    1033             : 
    1034          59 :     for (int iNextPoint = 0; iNextPoint < nTotalPoints;)
    1035             :     {
    1036          30 :         DGNPoint asPoints[MAX_ELEM_POINTS] = {};
    1037          30 :         int nThisCount = 0;
    1038             : 
    1039             :         // we need to repeat end points of elements.
    1040             :         // cppcheck-suppress duplicateExpression
    1041          30 :         if (iNextPoint != 0)
    1042           1 :             iNextPoint--;
    1043             : 
    1044         199 :         for (; iNextPoint < nTotalPoints && nThisCount < MAX_ELEM_POINTS;
    1045             :              iNextPoint++, nThisCount++)
    1046             :         {
    1047         169 :             asPoints[nThisCount].x = poLS->getX(iNextPoint);
    1048         169 :             asPoints[nThisCount].y = poLS->getY(iNextPoint);
    1049         169 :             asPoints[nThisCount].z = poLS->getZ(iNextPoint);
    1050             :         }
    1051             : 
    1052          30 :         if (nTotalPoints <= MAX_ELEM_POINTS)
    1053          28 :             papsGroup[0] =
    1054          28 :                 DGNCreateMultiPointElem(hDGN, nGroupType, nThisCount, asPoints);
    1055             :         else
    1056           2 :             papsGroup[++iGeom] = DGNCreateMultiPointElem(hDGN, DGNT_LINE_STRING,
    1057             :                                                          nThisCount, asPoints);
    1058             :     }
    1059             : 
    1060             :     /* -------------------------------------------------------------------- */
    1061             :     /*      We needed to make into a group.  Create the complex header      */
    1062             :     /*      from the rest of the group.                                     */
    1063             :     /* -------------------------------------------------------------------- */
    1064          29 :     if (papsGroup[0] == nullptr)
    1065             :     {
    1066           1 :         if (nGroupType == DGNT_SHAPE)
    1067           0 :             nGroupType = DGNT_COMPLEX_SHAPE_HEADER;
    1068             :         else
    1069           1 :             nGroupType = DGNT_COMPLEX_CHAIN_HEADER;
    1070             : 
    1071           1 :         papsGroup[0] = DGNCreateComplexHeaderFromGroup(hDGN, nGroupType, iGeom,
    1072             :                                                        papsGroup + 1);
    1073             :     }
    1074             : 
    1075          29 :     return papsGroup;
    1076             : }
    1077             : 
    1078             : /************************************************************************/
    1079             : /*                           TranslateLabel()                           */
    1080             : /*                                                                      */
    1081             : /*      Translate LABEL feature.                                        */
    1082             : /************************************************************************/
    1083             : 
    1084           2 : DGNElemCore **OGRDGNLayer::TranslateLabel(OGRFeature *poFeature)
    1085             : 
    1086             : {
    1087           2 :     OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
    1088           2 :     const char *pszText = poFeature->GetFieldAsString("Text");
    1089             : 
    1090           4 :     OGRStyleMgr oMgr;
    1091           2 :     oMgr.InitFromFeature(poFeature);
    1092           2 :     OGRStyleLabel *poLabel = reinterpret_cast<OGRStyleLabel *>(oMgr.GetPart(0));
    1093           2 :     if (poLabel != nullptr && poLabel->GetType() != OGRSTCLabel)
    1094             :     {
    1095           0 :         delete poLabel;
    1096           0 :         poLabel = nullptr;
    1097             :     }
    1098             : 
    1099           2 :     double dfRotation = 0.0;
    1100           2 :     double dfCharHeight = 100.0;
    1101           2 :     int nFontID = 1;  // 1 is the default font for DGN. Not 0.
    1102             : 
    1103           2 :     if (poLabel != nullptr)
    1104             :     {
    1105             :         GBool bDefault;
    1106             : 
    1107           1 :         if (poLabel->TextString(bDefault) != nullptr && !bDefault)
    1108           1 :             pszText = poLabel->TextString(bDefault);
    1109           1 :         dfRotation = poLabel->Angle(bDefault);
    1110             : 
    1111           1 :         poLabel->Size(bDefault);
    1112           1 :         if (!bDefault && poLabel->GetUnit() == OGRSTUGround)
    1113           0 :             dfCharHeight = poLabel->Size(bDefault);
    1114             :         // this part is really kind of bogus.
    1115           1 :         if (!bDefault && poLabel->GetUnit() == OGRSTUMM)
    1116           1 :             dfCharHeight = poLabel->Size(bDefault) / 1000.0;
    1117             : 
    1118             :         /* get font id */
    1119             :         static const char *const papszFontNumbers[] = {
    1120             :             "STANDARD=0",
    1121             :             "WORKING=1",
    1122             :             "FANCY=2",
    1123             :             "ENGINEERING=3",
    1124             :             "NEWZERO=4",
    1125             :             "STENCEL=5",
    1126             :             "USTN_FANCY=7",
    1127             :             "COMPRESSED=8",
    1128             :             "STENCEQ=9",
    1129             :             "hand=10",
    1130             :             "ARCH=11",
    1131             :             "ARCHB=12",
    1132             :             "IGES1001=15",
    1133             :             "IGES1002=16",
    1134             :             "IGES1003=17",
    1135             :             "CENTB=18",
    1136             :             "MICROS=19",
    1137             :             "ISOFRACTIONS=22",
    1138             :             "ITALICS=23",
    1139             :             "ISO30=24",
    1140             :             "GREEK=25",
    1141             :             "ISOREC=26",
    1142             :             "Isoeq=27",
    1143             :             "ISO_FONTLEFT=30",
    1144             :             "ISO_FONTRIGHT=31",
    1145             :             "INTL_ENGINEERING=32",
    1146             :             "INTL_WORKING=33",
    1147             :             "ISOITEQ=34",
    1148             :             "USTN FONT 26=36",
    1149             :             "ARCHITECTURAL=41",
    1150             :             "BLOCK_OUTLINE=42",
    1151             :             "LOW_RES_FILLED=43",
    1152             :             "UPPERCASE50",
    1153             :             "FONT060=60",
    1154             :             "din=61",
    1155             :             "dinit=62",
    1156             :             "helvl=63",
    1157             :             "HELVLIT=64",
    1158             :             "helv=65",
    1159             :             "HELVIT=66",
    1160             :             "cent=67",
    1161             :             "CENTIT=68",
    1162             :             "SCRIPT=69",
    1163             :             "MICROQ=76",
    1164             :             "dotfont=77",
    1165             :             "DOTIT=78",
    1166             :             "FONT092=92",
    1167             :             "FONT094=94",
    1168             :             "ANSI_SYMBOLS=100",
    1169             :             "FEATURE_CONTROL_SYSMBOLS=101",
    1170             :             "SYMB_FAST=102",
    1171             :             "INTL_ISO=105",
    1172             :             "INTL_ISO_EQUAL=106",
    1173             :             "INTL_ISO_ITALIC=107",
    1174             :             "INTL_ISO_ITALIC_EQUAL=108",
    1175             :             nullptr};
    1176             : 
    1177           1 :         const char *pszFontName = poLabel->FontName(bDefault);
    1178           1 :         if (!bDefault && pszFontName != nullptr)
    1179             :         {
    1180           1 :             const char *pszFontNumber = CSLFetchNameValue(
    1181             :                 const_cast<char **>(papszFontNumbers), pszFontName);
    1182             : 
    1183           1 :             if (pszFontNumber != nullptr)
    1184             :             {
    1185           1 :                 nFontID = atoi(pszFontNumber);
    1186             :             }
    1187             :         }
    1188             :     }
    1189             : 
    1190           2 :     std::string osText;
    1191           2 :     const auto &osEncoding = m_poDS->GetEncoding();
    1192           2 :     if (!osEncoding.empty() && osEncoding != CPL_ENC_UTF8)
    1193             :     {
    1194           1 :         osText = CPLString(pszText).Recode(CPL_ENC_UTF8, osEncoding.c_str());
    1195             :     }
    1196             :     else
    1197             :     {
    1198           1 :         osText = pszText;
    1199             :     }
    1200             : 
    1201             :     DGNElemCore **papsGroup =
    1202           2 :         static_cast<DGNElemCore **>(CPLCalloc(sizeof(void *), 2));
    1203           2 :     papsGroup[0] =
    1204           2 :         DGNCreateTextElem(hDGN, osText.c_str(), nFontID, DGNJ_LEFT_BOTTOM,
    1205             :                           dfCharHeight, dfCharHeight, dfRotation, nullptr,
    1206             :                           poPoint->getX(), poPoint->getY(), poPoint->getZ());
    1207             : 
    1208           2 :     if (poLabel)
    1209           1 :         delete poLabel;
    1210             : 
    1211           4 :     return papsGroup;
    1212             : }
    1213             : 
    1214             : /************************************************************************/
    1215             : /*                           ICreateFeature()                            */
    1216             : /*                                                                      */
    1217             : /*      Create a new feature and write to file.                         */
    1218             : /************************************************************************/
    1219             : 
    1220          72 : OGRErr OGRDGNLayer::ICreateFeature(OGRFeature *poFeature)
    1221             : 
    1222             : {
    1223          72 :     if (!bUpdate)
    1224             :     {
    1225           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1226             :                  "Attempt to create feature on read-only DGN file.");
    1227           0 :         return OGRERR_FAILURE;
    1228             :     }
    1229             : 
    1230          72 :     return CreateFeatureWithGeom(poFeature, poFeature->GetGeometryRef());
    1231             : }
    1232             : 
    1233             : /************************************************************************/
    1234             : /*                       CreateFeatureWithGeom()                        */
    1235             : /*                                                                      */
    1236             : /*      Create an element or element group from a given geometry and    */
    1237             : /*      the given feature.  This method recurses to handle              */
    1238             : /*      collections as essentially independent features.                */
    1239             : /************************************************************************/
    1240             : 
    1241          98 : OGRErr OGRDGNLayer::CreateFeatureWithGeom(OGRFeature *poFeature,
    1242             :                                           const OGRGeometry *poGeom)
    1243             : 
    1244             : {
    1245             : 
    1246          98 :     if (poGeom == nullptr || poGeom->IsEmpty())
    1247             :     {
    1248          36 :         CPLError(CE_Failure, CPLE_AppDefined,
    1249             :                  "Features with empty, geometry collection geometries not\n"
    1250             :                  "supported in DGN format.");
    1251          36 :         return OGRERR_FAILURE;
    1252             :     }
    1253             : 
    1254             :     /* -------------------------------------------------------------------- */
    1255             :     /*      Translate the geometry.                                         */
    1256             :     /* -------------------------------------------------------------------- */
    1257          62 :     DGNElemCore **papsGroup = nullptr;
    1258          62 :     const char *pszStyle = poFeature->GetStyleString();
    1259             : 
    1260          62 :     if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1261             :     {
    1262          16 :         const OGRPoint *poPoint = poGeom->toPoint();
    1263          16 :         const char *pszText = poFeature->GetFieldAsString("Text");
    1264             : 
    1265          16 :         if ((pszText == nullptr || strlen(pszText) == 0) &&
    1266           0 :             (pszStyle == nullptr || strstr(pszStyle, "LABEL") == nullptr))
    1267             :         {
    1268             :             // Treat a non text point as a degenerate line.
    1269          14 :             DGNPoint asPoints[2] = {};
    1270          14 :             asPoints[0].x = poPoint->getX();
    1271          14 :             asPoints[0].y = poPoint->getY();
    1272          14 :             asPoints[0].z = poPoint->getZ();
    1273          14 :             asPoints[1] = asPoints[0];
    1274             : 
    1275             :             papsGroup =
    1276          14 :                 static_cast<DGNElemCore **>(CPLCalloc(sizeof(void *), 2));
    1277          14 :             papsGroup[0] =
    1278          14 :                 DGNCreateMultiPointElem(hDGN, DGNT_LINE, 2, asPoints);
    1279             :         }
    1280             :         else
    1281             :         {
    1282           2 :             papsGroup = TranslateLabel(poFeature);
    1283             :         }
    1284             :     }
    1285          46 :     else if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    1286             :     {
    1287             :         papsGroup =
    1288          16 :             LineStringToElementGroup(poGeom->toLineString(), DGNT_LINE_STRING);
    1289             :     }
    1290          30 :     else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    1291             :     {
    1292          13 :         const OGRPolygon *poPoly = poGeom->toPolygon();
    1293             : 
    1294             :         DGNElemCore **papsGroupExt =
    1295          13 :             LineStringToElementGroup(poPoly->getExteriorRing(), DGNT_SHAPE);
    1296             : 
    1297          13 :         const int innerRingsCnt = poPoly->getNumInteriorRings();
    1298             : 
    1299          13 :         if (innerRingsCnt > 0)
    1300             :         {
    1301           0 :             CPLDebug("InnerRings", "there are %d inner rings", innerRingsCnt);
    1302           0 :             std::list<DGNElemCore *> dgnElements;
    1303             : 
    1304           0 :             for (int i = 0; papsGroupExt[i] != nullptr; i++)
    1305             :             {
    1306           0 :                 dgnElements.push_back(papsGroupExt[i]);
    1307             :             }
    1308           0 :             CPLFree(papsGroupExt);
    1309             : 
    1310             :             // get all interior rings and create complex group shape
    1311           0 :             for (int iRing = 0; iRing < innerRingsCnt; iRing++)
    1312             :             {
    1313           0 :                 DGNElemCore **papsGroupInner = LineStringToElementGroup(
    1314           0 :                     poPoly->getInteriorRing(iRing), DGNT_SHAPE);
    1315           0 :                 papsGroupInner[0]->properties |= DGNPF_HOLE;
    1316           0 :                 DGNUpdateElemCoreExtended(hDGN, papsGroupInner[0]);
    1317           0 :                 for (int i = 0; papsGroupInner[i] != nullptr; i++)
    1318             :                 {
    1319           0 :                     dgnElements.push_back(papsGroupInner[i]);
    1320             :                 }
    1321           0 :                 CPLFree(papsGroupInner);
    1322             :             }
    1323           0 :             int index = 1;
    1324             :             papsGroup = static_cast<DGNElemCore **>(
    1325           0 :                 CPLCalloc(sizeof(void *), dgnElements.size() + 2));
    1326           0 :             for (std::list<DGNElemCore *>::iterator list_iter =
    1327           0 :                      dgnElements.begin();
    1328           0 :                  list_iter != dgnElements.end(); ++list_iter)
    1329             :             {
    1330           0 :                 papsGroup[index++] = *list_iter;
    1331             :             }
    1332             : 
    1333             :             // papsGroup[0] = DGNCreateComplexHeaderFromGroup(
    1334             :             //     hDGN, DGNT_COMPLEX_SHAPE_HEADER, dgnElements.size(),
    1335             :             //     papsGroup+1 );
    1336           0 :             DGNPoint asPoints[1] = {};
    1337           0 :             papsGroup[0] = DGNCreateCellHeaderFromGroup(
    1338           0 :                 hDGN, "", 1, nullptr, static_cast<int>(dgnElements.size()),
    1339             :                 papsGroup + 1, asPoints + 0, 1.0, 1.0, 0.0);
    1340           0 :             DGNAddShapeFillInfo(hDGN, papsGroup[0], 6);
    1341             :         }
    1342             :         else
    1343             :         {
    1344          13 :             papsGroup = papsGroupExt;
    1345             :         }
    1346             :     }
    1347          17 :     else if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ||
    1348          13 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ||
    1349          35 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
    1350           5 :              wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
    1351             :     {
    1352          42 :         for (auto &&poMember : poGeom->toGeometryCollection())
    1353             :         {
    1354          26 :             OGRErr eErr = CreateFeatureWithGeom(poFeature, poMember);
    1355          26 :             if (eErr != OGRERR_NONE)
    1356           1 :                 return eErr;
    1357             :         }
    1358             : 
    1359          16 :         return OGRERR_NONE;
    1360             :     }
    1361             :     else
    1362             :     {
    1363           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1364             :                  "Unsupported geometry type (%s) for DGN.",
    1365           0 :                  OGRGeometryTypeToName(poGeom->getGeometryType()));
    1366           0 :         return OGRERR_FAILURE;
    1367             :     }
    1368             : 
    1369             :     /* -------------------------------------------------------------------- */
    1370             :     /*      Add other attributes.                                           */
    1371             :     /* -------------------------------------------------------------------- */
    1372          45 :     int nLevel = poFeature->GetFieldAsInteger("Level");
    1373          45 :     int nGraphicGroup = poFeature->GetFieldAsInteger("GraphicGroup");
    1374          45 :     int nColor = poFeature->GetFieldAsInteger("ColorIndex");
    1375          45 :     int nWeight = poFeature->GetFieldAsInteger("Weight");
    1376          45 :     int nStyle = poFeature->GetFieldAsInteger("Style");
    1377          45 :     int nMSLink = poFeature->GetFieldAsInteger("MSLink");
    1378             : 
    1379             :     // TODO: Use std::max and std::min.
    1380          45 :     nLevel = std::max(0, std::min(63, nLevel));
    1381          45 :     nColor = std::max(0, std::min(255, nColor));
    1382          45 :     nWeight = std::max(0, std::min(31, nWeight));
    1383          45 :     nStyle = std::max(0, std::min(7, nStyle));
    1384          45 :     nMSLink = std::max(0, nMSLink);
    1385             : 
    1386          45 :     DGNUpdateElemCore(hDGN, papsGroup[0], nLevel, nGraphicGroup, nColor,
    1387             :                       nWeight, nStyle);
    1388          45 :     DGNAddMSLink(hDGN, papsGroup[0], DGNLT_ODBC, 0, nMSLink);
    1389             :     /* -------------------------------------------------------------------- */
    1390             :     /*      Write to file.                                                  */
    1391             :     /* -------------------------------------------------------------------- */
    1392          92 :     for (int i = 0; papsGroup[i] != nullptr; i++)
    1393             :     {
    1394          47 :         DGNWriteElement(hDGN, papsGroup[i]);
    1395             : 
    1396          47 :         if (i == 0)
    1397          45 :             poFeature->SetFID(papsGroup[i]->element_id);
    1398             : 
    1399          47 :         DGNFreeElement(hDGN, papsGroup[i]);
    1400             :     }
    1401             : 
    1402          45 :     CPLFree(papsGroup);
    1403             : 
    1404          45 :     return OGRERR_NONE;
    1405             : }
    1406             : 
    1407             : /************************************************************************/
    1408             : /*                             GetDataset()                             */
    1409             : /************************************************************************/
    1410             : 
    1411          18 : GDALDataset *OGRDGNLayer::GetDataset()
    1412             : {
    1413          18 :     return m_poDS;
    1414             : }

Generated by: LCOV version 1.14