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

Generated by: LCOV version 1.14