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

Generated by: LCOV version 1.14