LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dgn - ogrdgnlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 391 569 68.7 %
Date: 2024-05-08 14:54:11 Functions: 13 16 81.2 %

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

Generated by: LCOV version 1.14