LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/cad - ogrcadlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 211 421 50.1 %
Date: 2025-05-31 00:00:17 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /*******************************************************************************
       2             :  *  Project: OGR CAD Driver
       3             :  *  Purpose: Implements driver based on libopencad
       4             :  *  Author: Alexandr Borzykh, mush3d at gmail.com
       5             :  *  Author: Dmitry Baryshnikov, polimax@mail.ru
       6             :  *  Language: C++
       7             :  *******************************************************************************
       8             :  *  The MIT License (MIT)
       9             :  *
      10             :  *  Copyright (c) 2016 Alexandr Borzykh
      11             :  *  Copyright (c) 2016-2019, NextGIS <info@nextgis.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  *******************************************************************************/
      15             : #include "cpl_conv.h"
      16             : #include "ogr_cad.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <iomanip>
      20             : #include <sstream>
      21             : 
      22             : #define FIELD_NAME_GEOMTYPE "cadgeom_type"
      23             : #define FIELD_NAME_THICKNESS "thickness"
      24             : #define FIELD_NAME_COLOR "color"
      25             : #define FIELD_NAME_EXT_DATA "extentity_data"
      26             : #define FIELD_NAME_TEXT "text"
      27             : 
      28             : constexpr double DEG2RAD = M_PI / 180.0;
      29             : constexpr double RAD2DEG = 1.0 / DEG2RAD;
      30             : 
      31          10 : OGRCADLayer::OGRCADLayer(GDALDataset *poDS, CADLayer &poCADLayer_,
      32          10 :                          OGRSpatialReference *poSR, int nEncoding)
      33             :     : m_poDS(poDS), poSpatialRef(poSR), poCADLayer(poCADLayer_),
      34          10 :       nDWGEncoding(nEncoding)
      35             : {
      36          10 :     nNextFID = 0;
      37             : 
      38          10 :     if (poSpatialRef)
      39          10 :         poSpatialRef->Reference();
      40          10 :     poFeatureDefn =
      41          10 :         new OGRFeatureDefn(CADRecode(poCADLayer_.getName(), nDWGEncoding));
      42             : 
      43             :     // Setting up layer geometry type
      44             :     OGRwkbGeometryType eGeomType;
      45          10 :     char dLineStringPresented = 0;
      46          10 :     char dCircularStringPresented = 0;
      47          10 :     char dPointPresented = 0;
      48          10 :     char dPolygonPresented = 0;
      49             :     std::vector<CADObject::ObjectType> aePresentedGeometryTypes =
      50          20 :         poCADLayer.getGeometryTypes();
      51          28 :     for (size_t i = 0; i < aePresentedGeometryTypes.size(); ++i)
      52             :     {
      53          18 :         switch (aePresentedGeometryTypes[i])
      54             :         {
      55          13 :             case CADObject::ATTDEF:
      56             :             case CADObject::TEXT:
      57             :             case CADObject::MTEXT:
      58             :             case CADObject::POINT:
      59          13 :                 dPointPresented = 1;
      60          13 :                 break;
      61           3 :             case CADObject::CIRCLE:
      62           3 :                 dCircularStringPresented = 1;
      63           3 :                 break;
      64           2 :             case CADObject::SPLINE:
      65             :             case CADObject::ELLIPSE:
      66             :             case CADObject::ARC:
      67             :             case CADObject::POLYLINE3D:
      68             :             case CADObject::POLYLINE2D:
      69             :             case CADObject::LWPOLYLINE:
      70             :             case CADObject::LINE:
      71           2 :                 dLineStringPresented = 1;
      72           2 :                 break;
      73           0 :             case CADObject::FACE3D:
      74             :             case CADObject::SOLID:
      75           0 :                 dPolygonPresented = 1;
      76           0 :                 break;
      77           0 :             default:
      78           0 :                 break;
      79             :         }
      80             :     }
      81             : 
      82          10 :     if ((dLineStringPresented + dCircularStringPresented + dPointPresented +
      83          10 :          dPolygonPresented) > 1)
      84             :     {
      85           0 :         eGeomType = wkbGeometryCollection;
      86             :     }
      87             :     else
      88             :     {
      89          10 :         if (dLineStringPresented)
      90             :         {
      91           2 :             eGeomType = wkbLineString;
      92             :         }
      93           8 :         else if (dCircularStringPresented)
      94             :         {
      95           3 :             eGeomType = wkbCircularString;
      96             :         }
      97           5 :         else if (dPointPresented)
      98             :         {
      99           5 :             eGeomType = wkbPoint;
     100             :         }
     101           0 :         else if (dPolygonPresented)
     102             :         {
     103           0 :             eGeomType = wkbPolygon;
     104             :         }
     105             :         else
     106             :         {
     107           0 :             eGeomType = wkbUnknown;
     108             :         }
     109             :     }
     110          10 :     poFeatureDefn->SetGeomType(eGeomType);
     111             : 
     112          20 :     OGRFieldDefn oClassField(FIELD_NAME_GEOMTYPE, OFTString);
     113          10 :     poFeatureDefn->AddFieldDefn(&oClassField);
     114             : 
     115          20 :     OGRFieldDefn oLinetypeField(FIELD_NAME_THICKNESS, OFTReal);
     116          10 :     poFeatureDefn->AddFieldDefn(&oLinetypeField);
     117             : 
     118          20 :     OGRFieldDefn oColorField(FIELD_NAME_COLOR, OFTString);
     119          10 :     poFeatureDefn->AddFieldDefn(&oColorField);
     120             : 
     121          20 :     OGRFieldDefn oExtendedField(FIELD_NAME_EXT_DATA, OFTString);
     122          10 :     poFeatureDefn->AddFieldDefn(&oExtendedField);
     123             : 
     124          20 :     OGRFieldDefn oTextField(FIELD_NAME_TEXT, OFTString);
     125          10 :     poFeatureDefn->AddFieldDefn(&oTextField);
     126             : 
     127          20 :     auto oAttrTags = poCADLayer.getAttributesTags();
     128          14 :     for (const std::string &osTag : oAttrTags)
     129             :     {
     130           4 :         auto ret = asFeaturesAttributes.insert(osTag);
     131           4 :         if (ret.second == true)
     132             :         {
     133           8 :             OGRFieldDefn oAttrField(osTag.c_str(), OFTString);
     134           4 :             poFeatureDefn->AddFieldDefn(&oAttrField);
     135             :         }
     136             :     }
     137             : 
     138             :     // Applying spatial ref info
     139          10 :     if (poFeatureDefn->GetGeomFieldCount() != 0)
     140          10 :         poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSpatialRef);
     141             : 
     142          10 :     SetDescription(poFeatureDefn->GetName());
     143          10 :     poFeatureDefn->Reference();
     144          10 : }
     145             : 
     146           7 : GIntBig OGRCADLayer::GetFeatureCount(int bForce)
     147             : {
     148           7 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
     149           0 :         return OGRLayer::GetFeatureCount(bForce);
     150             : 
     151           7 :     return poCADLayer.getGeometryCount();
     152             : }
     153             : 
     154           0 : int OGRCADLayer::TestCapability(const char *pszCap)
     155             : {
     156           0 :     if (EQUAL(pszCap, OLCMeasuredGeometries))
     157           0 :         return true;
     158           0 :     if (EQUAL(pszCap, OLCZGeometries))
     159           0 :         return true;
     160           0 :     if (EQUAL(pszCap, OLCCurveGeometries))
     161           0 :         return true;
     162             : 
     163           0 :     return FALSE;
     164             : }
     165             : 
     166          20 : OGRCADLayer::~OGRCADLayer()
     167             : {
     168          10 :     if (poSpatialRef)
     169          10 :         poSpatialRef->Release();
     170          10 :     poFeatureDefn->Release();
     171          20 : }
     172             : 
     173           4 : void OGRCADLayer::ResetReading()
     174             : {
     175           4 :     nNextFID = 0;
     176           4 : }
     177             : 
     178          13 : OGRFeature *OGRCADLayer::GetNextFeature()
     179             : {
     180          13 :     OGRFeature *poFeature = GetFeature(nNextFID);
     181          13 :     ++nNextFID;
     182             : 
     183          13 :     if (poFeature == nullptr)
     184           0 :         return nullptr;
     185             : 
     186          26 :     if ((m_poFilterGeom == nullptr ||
     187          26 :          FilterGeometry(poFeature->GetGeometryRef())) &&
     188          13 :         (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
     189             :     {
     190          13 :         return poFeature;
     191             :     }
     192             : 
     193           0 :     return nullptr;
     194             : }
     195             : 
     196          13 : OGRFeature *OGRCADLayer::GetFeature(GIntBig nFID)
     197             : {
     198          13 :     if (poCADLayer.getGeometryCount() <= static_cast<size_t>(nFID) || nFID < 0)
     199             :     {
     200           0 :         return nullptr;
     201             :     }
     202             : 
     203          13 :     OGRFeature *poFeature = nullptr;
     204             :     CADGeometry *poCADGeometry =
     205          13 :         poCADLayer.getGeometry(static_cast<size_t>(nFID));
     206             : 
     207          26 :     if (nullptr == poCADGeometry ||
     208          13 :         GetLastErrorCode() != CADErrorCodes::SUCCESS)
     209             :     {
     210           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     211             :                  "Failed to get geometry with ID = " CPL_FRMT_GIB
     212             :                  " from layer \"%s\". Libopencad errorcode: %d",
     213           0 :                  nFID, poCADLayer.getName().c_str(), GetLastErrorCode());
     214           0 :         return nullptr;
     215             :     }
     216             : 
     217          13 :     poFeature = new OGRFeature(poFeatureDefn);
     218          13 :     poFeature->SetFID(nFID);
     219          13 :     poFeature->SetField(FIELD_NAME_THICKNESS, poCADGeometry->getThickness());
     220             : 
     221          13 :     if (!poCADGeometry->getEED().empty())
     222             :     {
     223           6 :         std::vector<std::string> asGeometryEED = poCADGeometry->getEED();
     224           6 :         std::string sEEDAsOneString = "";
     225           3 :         for (std::vector<std::string>::const_iterator iter =
     226           3 :                  asGeometryEED.cbegin();
     227           9 :              iter != asGeometryEED.cend(); ++iter)
     228             :         {
     229           3 :             sEEDAsOneString += *iter;
     230           3 :             sEEDAsOneString += ' ';
     231             :         }
     232             : 
     233           3 :         poFeature->SetField(FIELD_NAME_EXT_DATA, sEEDAsOneString.c_str());
     234             :     }
     235             : 
     236          13 :     RGBColor stRGB = poCADGeometry->getColor();
     237          26 :     CPLString sHexColor;
     238          13 :     sHexColor.Printf("#%02X%02X%02X%02X", stRGB.R, stRGB.G, stRGB.B, 255);
     239          13 :     poFeature->SetField(FIELD_NAME_COLOR, sHexColor);
     240             : 
     241          26 :     CPLString sStyle;
     242          13 :     sStyle.Printf("PEN(c:%s,w:5px)", sHexColor.c_str());
     243          13 :     poFeature->SetStyleString(sStyle);
     244             : 
     245          26 :     std::vector<CADAttrib> oBlockAttrs = poCADGeometry->getBlockAttributes();
     246          13 :     for (const CADAttrib &oAttrib : oBlockAttrs)
     247             :     {
     248           0 :         CPLString osTag = oAttrib.getTag();
     249           0 :         auto featureAttrIt = asFeaturesAttributes.find(osTag);
     250           0 :         if (featureAttrIt != asFeaturesAttributes.end())
     251             :         {
     252           0 :             poFeature->SetField(*featureAttrIt, oAttrib.getTextValue().c_str());
     253             :         }
     254             :     }
     255             : 
     256          13 :     switch (poCADGeometry->getType())
     257             :     {
     258           1 :         case CADGeometry::POINT:
     259             :         {
     260             :             CADPoint3D *const poCADPoint =
     261           1 :                 cpl::down_cast<CADPoint3D *>(poCADGeometry);
     262           1 :             CADVector stPositionVector = poCADPoint->getPosition();
     263             : 
     264           1 :             poFeature->SetGeometryDirectly(
     265           1 :                 new OGRPoint(stPositionVector.getX(), stPositionVector.getY(),
     266           1 :                              stPositionVector.getZ()));
     267           1 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADPoint");
     268           1 :             break;
     269             :         }
     270             : 
     271           1 :         case CADGeometry::LINE:
     272             :         {
     273           1 :             CADLine *const poCADLine = cpl::down_cast<CADLine *>(poCADGeometry);
     274           1 :             OGRLineString *poLS = new OGRLineString();
     275           2 :             poLS->addPoint(poCADLine->getStart().getPosition().getX(),
     276           2 :                            poCADLine->getStart().getPosition().getY(),
     277           2 :                            poCADLine->getStart().getPosition().getZ());
     278           2 :             poLS->addPoint(poCADLine->getEnd().getPosition().getX(),
     279           2 :                            poCADLine->getEnd().getPosition().getY(),
     280           2 :                            poCADLine->getEnd().getPosition().getZ());
     281             : 
     282           1 :             poFeature->SetGeometryDirectly(poLS);
     283           1 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADLine");
     284           1 :             break;
     285             :         }
     286             : 
     287           0 :         case CADGeometry::SOLID:
     288             :         {
     289             :             CADSolid *const poCADSolid =
     290           0 :                 cpl::down_cast<CADSolid *>(poCADGeometry);
     291           0 :             OGRPolygon *poPoly = new OGRPolygon();
     292           0 :             OGRLinearRing *poLR = new OGRLinearRing();
     293             : 
     294           0 :             std::vector<CADVector> astSolidCorners = poCADSolid->getCorners();
     295           0 :             for (size_t i = 0; i < astSolidCorners.size(); ++i)
     296             :             {
     297           0 :                 poLR->addPoint(astSolidCorners[i].getX(),
     298           0 :                                astSolidCorners[i].getY(),
     299           0 :                                astSolidCorners[i].getZ());
     300             :             }
     301           0 :             poPoly->addRingDirectly(poLR);
     302           0 :             poPoly->closeRings();
     303           0 :             poFeature->SetGeometryDirectly(poPoly);
     304             : 
     305           0 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADSolid");
     306           0 :             break;
     307             :         }
     308             : 
     309           3 :         case CADGeometry::CIRCLE:
     310             :         {
     311           3 :             CADCircle *poCADCircle = cpl::down_cast<CADCircle *>(poCADGeometry);
     312           3 :             OGRCircularString *poCircle = new OGRCircularString();
     313             : 
     314           3 :             CADVector stCircleCenter = poCADCircle->getPosition();
     315           6 :             OGRPoint oCirclePoint1;
     316           3 :             oCirclePoint1.setX(stCircleCenter.getX() -
     317           3 :                                poCADCircle->getRadius());
     318           3 :             oCirclePoint1.setY(stCircleCenter.getY());
     319           3 :             oCirclePoint1.setZ(stCircleCenter.getZ());
     320           3 :             poCircle->addPoint(&oCirclePoint1);
     321             : 
     322           6 :             OGRPoint oCirclePoint2;
     323           3 :             oCirclePoint2.setX(stCircleCenter.getX());
     324           3 :             oCirclePoint2.setY(stCircleCenter.getY() +
     325           3 :                                poCADCircle->getRadius());
     326           3 :             oCirclePoint2.setZ(stCircleCenter.getZ());
     327           3 :             poCircle->addPoint(&oCirclePoint2);
     328             : 
     329           6 :             OGRPoint oCirclePoint3;
     330           3 :             oCirclePoint3.setX(stCircleCenter.getX() +
     331           3 :                                poCADCircle->getRadius());
     332           3 :             oCirclePoint3.setY(stCircleCenter.getY());
     333           3 :             oCirclePoint3.setZ(stCircleCenter.getZ());
     334           3 :             poCircle->addPoint(&oCirclePoint3);
     335             : 
     336           6 :             OGRPoint oCirclePoint4;
     337           3 :             oCirclePoint4.setX(stCircleCenter.getX());
     338           3 :             oCirclePoint4.setY(stCircleCenter.getY() -
     339           3 :                                poCADCircle->getRadius());
     340           3 :             oCirclePoint4.setZ(stCircleCenter.getZ());
     341           3 :             poCircle->addPoint(&oCirclePoint4);
     342             : 
     343             :             // Close the circle
     344           3 :             poCircle->addPoint(&oCirclePoint1);
     345             : 
     346             :             /*NOTE: The alternative way:
     347             :                     OGRGeometry *poCircle =
     348             :                OGRGeometryFactory::approximateArcAngles(
     349             :                     poCADCircle->getPosition().getX(),
     350             :                     poCADCircle->getPosition().getY(),
     351             :                     poCADCircle->getPosition().getZ(),
     352             :                     poCADCircle->getRadius(), poCADCircle->getRadius(), 0.0,
     353             :                     0.0, 360.0,
     354             :                     0.0 );
     355             :             */
     356           3 :             poFeature->SetGeometryDirectly(poCircle);
     357             : 
     358           3 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADCircle");
     359           3 :             break;
     360             :         }
     361             : 
     362           0 :         case CADGeometry::ARC:
     363             :         {
     364           0 :             CADArc *poCADArc = cpl::down_cast<CADArc *>(poCADGeometry);
     365           0 :             OGRCircularString *poCircle = new OGRCircularString();
     366             : 
     367             :             // Need at least 3 points in arc
     368           0 :             double dfStartAngle = poCADArc->getStartingAngle() * RAD2DEG;
     369           0 :             double dfEndAngle = poCADArc->getEndingAngle() * RAD2DEG;
     370           0 :             double dfMidAngle = (dfEndAngle + dfStartAngle) / 2;
     371           0 :             CADVector stCircleCenter = poCADArc->getPosition();
     372             : 
     373           0 :             OGRPoint oCirclePoint;
     374           0 :             oCirclePoint.setX(stCircleCenter.getX() +
     375           0 :                               poCADArc->getRadius() * cos(dfStartAngle));
     376           0 :             oCirclePoint.setY(stCircleCenter.getY() +
     377           0 :                               poCADArc->getRadius() * sin(dfStartAngle));
     378           0 :             oCirclePoint.setZ(stCircleCenter.getZ());
     379           0 :             poCircle->addPoint(&oCirclePoint);
     380             : 
     381           0 :             oCirclePoint.setX(stCircleCenter.getX() +
     382           0 :                               poCADArc->getRadius() * cos(dfMidAngle));
     383           0 :             oCirclePoint.setY(stCircleCenter.getY() +
     384           0 :                               poCADArc->getRadius() * sin(dfMidAngle));
     385           0 :             oCirclePoint.setZ(stCircleCenter.getZ());
     386           0 :             poCircle->addPoint(&oCirclePoint);
     387             : 
     388           0 :             oCirclePoint.setX(stCircleCenter.getX() +
     389           0 :                               poCADArc->getRadius() * cos(dfEndAngle));
     390           0 :             oCirclePoint.setY(stCircleCenter.getY() +
     391           0 :                               poCADArc->getRadius() * sin(dfEndAngle));
     392           0 :             oCirclePoint.setZ(stCircleCenter.getZ());
     393           0 :             poCircle->addPoint(&oCirclePoint);
     394             : 
     395             :             /*NOTE: alternative way:
     396             :                 OGRGeometry * poArc = OGRGeometryFactory::approximateArcAngles(
     397             :                 poCADArc->getPosition().getX(),
     398             :                 poCADArc->getPosition().getY(),
     399             :                 poCADArc->getPosition().getZ(),
     400             :                 poCADArc->getRadius(), poCADArc->getRadius(), 0.0,
     401             :                 dfStartAngle,
     402             :                 dfStartAngle > dfEndAngle ?
     403             :                     ( dfEndAngle + 360.0f ) :
     404             :                     dfEndAngle,
     405             :                 0.0 );
     406             :             */
     407             : 
     408           0 :             poFeature->SetGeometryDirectly(poCircle);
     409           0 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADArc");
     410             : 
     411           0 :             break;
     412             :         }
     413             : 
     414           0 :         case CADGeometry::FACE3D:
     415             :         {
     416             :             CADFace3D *const poCADFace =
     417           0 :                 cpl::down_cast<CADFace3D *>(poCADGeometry);
     418           0 :             OGRPolygon *poPoly = new OGRPolygon();
     419           0 :             OGRLinearRing *poLR = new OGRLinearRing();
     420             : 
     421           0 :             for (size_t i = 0; i < 3; ++i)
     422             :             {
     423           0 :                 poLR->addPoint(poCADFace->getCorner(i).getX(),
     424           0 :                                poCADFace->getCorner(i).getY(),
     425           0 :                                poCADFace->getCorner(i).getZ());
     426             :             }
     427           0 :             if (!(poCADFace->getCorner(2) == poCADFace->getCorner(3)))
     428             :             {
     429           0 :                 poLR->addPoint(poCADFace->getCorner(3).getX(),
     430           0 :                                poCADFace->getCorner(3).getY(),
     431           0 :                                poCADFace->getCorner(3).getZ());
     432             :             }
     433           0 :             poPoly->addRingDirectly(poLR);
     434           0 :             poPoly->closeRings();
     435           0 :             poFeature->SetGeometryDirectly(poPoly);
     436             : 
     437           0 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADFace3D");
     438           0 :             break;
     439             :         }
     440             : 
     441           0 :         case CADGeometry::LWPOLYLINE:
     442             :         {
     443             :             CADLWPolyline *const poCADLWPolyline =
     444           0 :                 cpl::down_cast<CADLWPolyline *>(poCADGeometry);
     445             : 
     446           0 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADLWPolyline");
     447             : 
     448             :             /*
     449             :              * Excessive check, like in DXF driver.
     450             :              * I tried to make a single-point polyline, but couldn't make it.
     451             :              * Probably this check should be removed.
     452             :              */
     453           0 :             if (poCADLWPolyline->getVertexCount() == 1)
     454             :             {
     455           0 :                 poFeature->SetGeometryDirectly(
     456           0 :                     new OGRPoint(poCADLWPolyline->getVertex(0).getX(),
     457           0 :                                  poCADLWPolyline->getVertex(0).getY(),
     458           0 :                                  poCADLWPolyline->getVertex(0).getZ()));
     459             : 
     460           0 :                 break;
     461             :             }
     462             : 
     463             :             /*
     464             :              * If polyline has no arcs, handle it in easy way.
     465             :              */
     466           0 :             OGRLineString *poLS = new OGRLineString();
     467             : 
     468           0 :             if (poCADLWPolyline->getBulges().empty())
     469             :             {
     470           0 :                 for (size_t i = 0; i < poCADLWPolyline->getVertexCount(); ++i)
     471             :                 {
     472           0 :                     CADVector stVertex = poCADLWPolyline->getVertex(i);
     473           0 :                     poLS->addPoint(stVertex.getX(), stVertex.getY(),
     474             :                                    stVertex.getZ());
     475             :                 }
     476             : 
     477           0 :                 poFeature->SetGeometryDirectly(poLS);
     478           0 :                 break;
     479             :             }
     480             : 
     481             :             /*
     482             :              * Last case - if polyline has mixed arcs and lines.
     483             :              */
     484           0 :             bool bLineStringStarted = false;
     485           0 :             std::vector<double> adfBulges = poCADLWPolyline->getBulges();
     486             :             const size_t nCount =
     487           0 :                 std::min(adfBulges.size(), poCADLWPolyline->getVertexCount());
     488             : 
     489           0 :             for (size_t iCurrentVertex = 0; iCurrentVertex + 1 < nCount;
     490             :                  iCurrentVertex++)
     491             :             {
     492             :                 CADVector stCurrentVertex =
     493           0 :                     poCADLWPolyline->getVertex(iCurrentVertex);
     494             :                 CADVector stNextVertex =
     495           0 :                     poCADLWPolyline->getVertex(iCurrentVertex + 1);
     496             : 
     497             :                 double dfLength =
     498           0 :                     sqrt(pow(stNextVertex.getX() - stCurrentVertex.getX(), 2) +
     499           0 :                          pow(stNextVertex.getY() - stCurrentVertex.getY(), 2));
     500             : 
     501             :                 /*
     502             :                  * Handling straight polyline segment.
     503             :                  */
     504           0 :                 if ((dfLength == 0) || (adfBulges[iCurrentVertex] == 0))
     505             :                 {
     506           0 :                     if (!bLineStringStarted)
     507             :                     {
     508           0 :                         poLS->addPoint(stCurrentVertex.getX(),
     509             :                                        stCurrentVertex.getY(),
     510             :                                        stCurrentVertex.getZ());
     511           0 :                         bLineStringStarted = true;
     512             :                     }
     513             : 
     514           0 :                     poLS->addPoint(stNextVertex.getX(), stNextVertex.getY(),
     515             :                                    stNextVertex.getZ());
     516             :                 }
     517             :                 else
     518             :                 {
     519           0 :                     double dfSegmentBulge = adfBulges[iCurrentVertex];
     520           0 :                     double dfH = (dfSegmentBulge * dfLength) / 2;
     521           0 :                     if (dfH == 0.0)
     522           0 :                         dfH = 1.0;  // just to avoid a division by zero
     523           0 :                     double dfRadius =
     524           0 :                         (dfH / 2) + (dfLength * dfLength / (8 * dfH));
     525           0 :                     double dfOgrArcRotation = 0,
     526           0 :                            dfOgrArcRadius = fabs(dfRadius);
     527             : 
     528             :                     /*
     529             :                      * Set arc's direction and keep bulge positive.
     530             :                      */
     531           0 :                     bool bClockwise = (dfSegmentBulge < 0);
     532           0 :                     if (bClockwise)
     533           0 :                         dfSegmentBulge *= -1;
     534             : 
     535             :                     /*
     536             :                      * Get arc's center point.
     537             :                      */
     538           0 :                     double dfSaggita = fabs(dfSegmentBulge * (dfLength / 2.0));
     539           0 :                     double dfApo = bClockwise ? -(dfOgrArcRadius - dfSaggita)
     540           0 :                                               : -(dfSaggita - dfOgrArcRadius);
     541             : 
     542           0 :                     CADVector stVertex;
     543           0 :                     stVertex.setX(stCurrentVertex.getX() - stNextVertex.getX());
     544           0 :                     stVertex.setY(stCurrentVertex.getY() - stNextVertex.getY());
     545           0 :                     stVertex.setZ(stCurrentVertex.getZ());
     546             : 
     547           0 :                     CADVector stMidPoint;
     548           0 :                     stMidPoint.setX(stNextVertex.getX() +
     549           0 :                                     0.5 * stVertex.getX());
     550           0 :                     stMidPoint.setY(stNextVertex.getY() +
     551           0 :                                     0.5 * stVertex.getY());
     552           0 :                     stMidPoint.setZ(stVertex.getZ());
     553             : 
     554           0 :                     CADVector stPperp;
     555           0 :                     stPperp.setX(stVertex.getY());
     556           0 :                     stPperp.setY(-stVertex.getX());
     557             :                     double dfStPperpLength =
     558           0 :                         sqrt(stPperp.getX() * stPperp.getX() +
     559           0 :                              stPperp.getY() * stPperp.getY());
     560             :                     // TODO: Check that length isnot 0
     561           0 :                     stPperp.setX(stPperp.getX() / dfStPperpLength);
     562           0 :                     stPperp.setY(stPperp.getY() / dfStPperpLength);
     563             : 
     564           0 :                     CADVector stOgrArcCenter;
     565           0 :                     stOgrArcCenter.setX(stMidPoint.getX() +
     566           0 :                                         (stPperp.getX() * dfApo));
     567           0 :                     stOgrArcCenter.setY(stMidPoint.getY() +
     568           0 :                                         (stPperp.getY() * dfApo));
     569             : 
     570             :                     /*
     571             :                      * Get the line's general vertical direction ( -1 = down, +1
     572             :                      * = up ).
     573             :                      */
     574             :                     double dfLineDir =
     575           0 :                         stNextVertex.getY() > stCurrentVertex.getY() ? 1.0f
     576           0 :                                                                      : -1.0f;
     577             : 
     578             :                     /*
     579             :                      * Get arc's starting angle.
     580             :                      */
     581             :                     double dfA =
     582           0 :                         atan2(
     583           0 :                             (stOgrArcCenter.getY() - stCurrentVertex.getY()),
     584           0 :                             (stOgrArcCenter.getX() - stCurrentVertex.getX())) *
     585           0 :                         RAD2DEG;
     586           0 :                     if (bClockwise && (dfLineDir == 1.0))
     587           0 :                         dfA += (dfLineDir * 180.0);
     588             : 
     589           0 :                     double dfOgrArcStartAngle =
     590           0 :                         dfA > 0.0 ? -(dfA - 180.0) : -(dfA + 180.0);
     591             : 
     592             :                     /*
     593             :                      * Get arc's ending angle.
     594             :                      */
     595           0 :                     dfA = atan2((stOgrArcCenter.getY() - stNextVertex.getY()),
     596           0 :                                 (stOgrArcCenter.getX() - stNextVertex.getX())) *
     597             :                           RAD2DEG;
     598           0 :                     if (bClockwise && (dfLineDir == 1.0))
     599           0 :                         dfA += (dfLineDir * 180.0);
     600             : 
     601           0 :                     double dfOgrArcEndAngle =
     602           0 :                         dfA > 0.0 ? -(dfA - 180.0) : -(dfA + 180.0);
     603             : 
     604           0 :                     if (!bClockwise && (dfOgrArcStartAngle < dfOgrArcEndAngle))
     605           0 :                         dfOgrArcEndAngle = -180.0 + (dfLineDir * dfA);
     606             : 
     607           0 :                     if (bClockwise && (dfOgrArcStartAngle > dfOgrArcEndAngle))
     608           0 :                         dfOgrArcEndAngle += 360.0;
     609             : 
     610             :                     /*
     611             :                      * Flip arc's rotation if necessary.
     612             :                      */
     613           0 :                     if (bClockwise && (dfLineDir == 1.0))
     614           0 :                         dfOgrArcRotation = dfLineDir * 180.0;
     615             : 
     616             :                     /*
     617             :                      * Tessellate the arc segment and append to the linestring.
     618             :                      */
     619             :                     OGRLineString *poArcpoLS =
     620             :                         OGRGeometryFactory::approximateArcAngles(
     621             :                             stOgrArcCenter.getX(), stOgrArcCenter.getY(),
     622             :                             stOgrArcCenter.getZ(), dfOgrArcRadius,
     623             :                             dfOgrArcRadius, dfOgrArcRotation,
     624             :                             dfOgrArcStartAngle, dfOgrArcEndAngle, 0.0)
     625           0 :                             ->toLineString();
     626             : 
     627           0 :                     poLS->addSubLineString(poArcpoLS);
     628             : 
     629           0 :                     delete (poArcpoLS);
     630             :                 }
     631             :             }
     632             : 
     633           0 :             if (poCADLWPolyline->isClosed())
     634             :             {
     635           0 :                 poLS->addPoint(poCADLWPolyline->getVertex(0).getX(),
     636           0 :                                poCADLWPolyline->getVertex(0).getY(),
     637           0 :                                poCADLWPolyline->getVertex(0).getZ());
     638             :             }
     639             : 
     640           0 :             poFeature->SetGeometryDirectly(poLS);
     641           0 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADLWPolyline");
     642           0 :             break;
     643             :         }
     644             : 
     645             :         // TODO: Unsupported smooth lines
     646           0 :         case CADGeometry::POLYLINE3D:
     647             :         {
     648             :             CADPolyline3D *const poCADPolyline3D =
     649           0 :                 cpl::down_cast<CADPolyline3D *>(poCADGeometry);
     650           0 :             OGRLineString *poLS = new OGRLineString();
     651             : 
     652           0 :             for (size_t i = 0; i < poCADPolyline3D->getVertexCount(); ++i)
     653             :             {
     654           0 :                 CADVector stVertex = poCADPolyline3D->getVertex(i);
     655             : 
     656           0 :                 poLS->addPoint(stVertex.getX(), stVertex.getY(),
     657             :                                stVertex.getZ());
     658             :             }
     659             : 
     660           0 :             poFeature->SetGeometryDirectly(poLS);
     661           0 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADPolyline3D");
     662           0 :             break;
     663             :         }
     664             : 
     665           4 :         case CADGeometry::TEXT:
     666             :         {
     667           4 :             CADText *const poCADText = cpl::down_cast<CADText *>(poCADGeometry);
     668           4 :             OGRPoint *poPoint = new OGRPoint(poCADText->getPosition().getX(),
     669           4 :                                              poCADText->getPosition().getY(),
     670           4 :                                              poCADText->getPosition().getZ());
     671             :             CPLString sTextValue =
     672          12 :                 CADRecode(poCADText->getTextValue(), nDWGEncoding);
     673             : 
     674           4 :             poFeature->SetField(FIELD_NAME_TEXT, sTextValue);
     675           4 :             poFeature->SetGeometryDirectly(poPoint);
     676           4 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADText");
     677             : 
     678             :             sStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",c:%s)",
     679           4 :                           sTextValue.c_str(), sHexColor.c_str());
     680           4 :             poFeature->SetStyleString(sStyle);
     681           4 :             break;
     682             :         }
     683             : 
     684           2 :         case CADGeometry::MTEXT:
     685             :         {
     686             :             CADMText *const poCADMText =
     687           2 :                 cpl::down_cast<CADMText *>(poCADGeometry);
     688           2 :             OGRPoint *poPoint = new OGRPoint(poCADMText->getPosition().getX(),
     689           2 :                                              poCADMText->getPosition().getY(),
     690           2 :                                              poCADMText->getPosition().getZ());
     691             :             CPLString sTextValue =
     692           6 :                 CADRecode(poCADMText->getTextValue(), nDWGEncoding);
     693             : 
     694           2 :             poFeature->SetField(FIELD_NAME_TEXT, sTextValue);
     695           2 :             poFeature->SetGeometryDirectly(poPoint);
     696           2 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADMText");
     697             : 
     698             :             sStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",c:%s)",
     699           2 :                           sTextValue.c_str(), sHexColor.c_str());
     700           2 :             poFeature->SetStyleString(sStyle);
     701           2 :             break;
     702             :         }
     703             : 
     704           0 :         case CADGeometry::SPLINE:
     705             :         {
     706             :             CADSpline *const poCADSpline =
     707           0 :                 cpl::down_cast<CADSpline *>(poCADGeometry);
     708           0 :             OGRLineString *poLS = new OGRLineString();
     709             : 
     710             :             // TODO: Interpolate spline as points or curves
     711           0 :             for (size_t i = 0; i < poCADSpline->getControlPoints().size(); ++i)
     712             :             {
     713           0 :                 poLS->addPoint(poCADSpline->getControlPoints()[i].getX(),
     714           0 :                                poCADSpline->getControlPoints()[i].getY(),
     715           0 :                                poCADSpline->getControlPoints()[i].getZ());
     716             :             }
     717             : 
     718           0 :             poFeature->SetGeometryDirectly(poLS);
     719           0 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADSpline");
     720           0 :             break;
     721             :         }
     722             : 
     723           1 :         case CADGeometry::ELLIPSE:
     724             :         {
     725             :             CADEllipse *poCADEllipse =
     726           1 :                 cpl::down_cast<CADEllipse *>(poCADGeometry);
     727             : 
     728             :             // FIXME: Start/end angles should be swapped to work exactly as DXF
     729             :             // driver. is it correct?
     730           1 :             double dfStartAngle = poCADEllipse->getStartingAngle() * RAD2DEG;
     731           1 :             double dfEndAngle = poCADEllipse->getEndingAngle() * RAD2DEG;
     732           1 :             if (dfStartAngle > dfEndAngle)
     733             :             {
     734           0 :                 dfEndAngle += 360.0;
     735             :             }
     736           1 :             double dfAxisRatio = poCADEllipse->getAxisRatio();
     737             : 
     738           1 :             CADVector stEllipseCenter = poCADEllipse->getPosition();
     739           1 :             CADVector vectSMAxis = poCADEllipse->getSMAxis();
     740             :             double dfPrimaryRadius, dfSecondaryRadius;
     741             :             double dfRotation;
     742           1 :             dfPrimaryRadius = sqrt(vectSMAxis.getX() * vectSMAxis.getX() +
     743           1 :                                    vectSMAxis.getY() * vectSMAxis.getY() +
     744           1 :                                    vectSMAxis.getZ() * vectSMAxis.getZ());
     745             : 
     746           1 :             dfSecondaryRadius = dfAxisRatio * dfPrimaryRadius;
     747             : 
     748           1 :             dfRotation =
     749           1 :                 -1 * atan2(vectSMAxis.getY(), vectSMAxis.getX()) * RAD2DEG;
     750             :             /* NOTE: alternative way:
     751             :             OGRCircularString * poEllipse = new OGRCircularString();
     752             :             OGRPoint  oEllipsePoint1;
     753             :             oEllipsePoint1.setX( stEllipseCenter.getX() - dfPrimaryRadius *
     754             :                                                             cos( dfRotation ) );
     755             :             oEllipsePoint1.setY( stEllipseCenter.getY() + dfPrimaryRadius *
     756             :                                                             sin( dfRotation ) );
     757             :             oEllipsePoint1.setZ( stEllipseCenter.getZ() );
     758             :             poEllipse->addPoint( &oEllipsePoint1 );
     759             : 
     760             :             OGRPoint  oEllipsePoint2;
     761             :             oEllipsePoint2.setX( stEllipseCenter.getX() + dfSecondaryRadius *
     762             :                                                             cos( dfRotation ) );
     763             :             oEllipsePoint2.setY( stEllipseCenter.getY() + dfSecondaryRadius *
     764             :                                                             sin( dfRotation ) );
     765             :             oEllipsePoint2.setZ( stEllipseCenter.getZ() );
     766             :             poEllipse->addPoint( &oEllipsePoint2 );
     767             : 
     768             :             OGRPoint  oEllipsePoint3;
     769             :             oEllipsePoint3.setX( stEllipseCenter.getX() + dfPrimaryRadius *
     770             :                                                             cos( dfRotation ) );
     771             :             oEllipsePoint3.setY( stEllipseCenter.getY() - dfPrimaryRadius *
     772             :                                                             sin( dfRotation ) );
     773             :             oEllipsePoint3.setZ( stEllipseCenter.getZ() );
     774             :             poEllipse->addPoint( &oEllipsePoint3 );
     775             : 
     776             :             OGRPoint  oEllipsePoint4;
     777             :             oEllipsePoint4.setX( stEllipseCenter.getX() - dfSecondaryRadius *
     778             :                                                             cos( dfRotation ) );
     779             :             oEllipsePoint4.setY( stEllipseCenter.getY() - dfSecondaryRadius *
     780             :                                                             sin( dfRotation ) );
     781             :             oEllipsePoint4.setZ( stEllipseCenter.getZ() );
     782             :             poEllipse->addPoint( &oEllipsePoint4 );
     783             : 
     784             :             // Close the ellipse
     785             :             poEllipse->addPoint( &oEllipsePoint1 );
     786             :             */
     787             : 
     788           1 :             CPLDebug("CAD",
     789             :                      "Position: %f, %f, %f, radius %f/%f, start angle: %f, end "
     790             :                      "angle: %f, rotation: %f",
     791             :                      stEllipseCenter.getX(), stEllipseCenter.getY(),
     792             :                      stEllipseCenter.getZ(), dfPrimaryRadius, dfSecondaryRadius,
     793             :                      dfStartAngle, dfEndAngle, dfRotation);
     794           1 :             OGRGeometry *poEllipse = OGRGeometryFactory::approximateArcAngles(
     795             :                 stEllipseCenter.getX(), stEllipseCenter.getY(),
     796             :                 stEllipseCenter.getZ(), dfPrimaryRadius, dfSecondaryRadius,
     797             :                 dfRotation, dfStartAngle, dfEndAngle, 0.0);
     798             : 
     799           1 :             poFeature->SetGeometryDirectly(poEllipse);
     800           1 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADEllipse");
     801           1 :             break;
     802             :         }
     803             : 
     804           1 :         case CADGeometry::ATTDEF:
     805             :         {
     806             :             CADAttdef *const poCADAttdef =
     807           1 :                 cpl::down_cast<CADAttdef *>(poCADGeometry);
     808           1 :             OGRPoint *poPoint = new OGRPoint(poCADAttdef->getPosition().getX(),
     809           1 :                                              poCADAttdef->getPosition().getY(),
     810           1 :                                              poCADAttdef->getPosition().getZ());
     811             :             CPLString sTextValue =
     812           3 :                 CADRecode(poCADAttdef->getTag(), nDWGEncoding);
     813             : 
     814           1 :             poFeature->SetField(FIELD_NAME_TEXT, sTextValue);
     815           1 :             poFeature->SetGeometryDirectly(poPoint);
     816           1 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADAttdef");
     817             : 
     818             :             sStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",c:%s)",
     819           1 :                           sTextValue.c_str(), sHexColor.c_str());
     820           1 :             poFeature->SetStyleString(sStyle);
     821           1 :             break;
     822             :         }
     823             : 
     824           0 :         default:
     825             :         {
     826           0 :             CPLError(CE_Warning, CPLE_NotSupported,
     827             :                      "Unhandled feature. Skipping it.");
     828             : 
     829           0 :             poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADUnknown");
     830           0 :             delete poCADGeometry;
     831           0 :             return poFeature;
     832             :         }
     833             :     }
     834             : 
     835          13 :     delete poCADGeometry;
     836          13 :     poFeature->GetGeometryRef()->assignSpatialReference(poSpatialRef);
     837          13 :     return poFeature;
     838             : }

Generated by: LCOV version 1.14