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

Generated by: LCOV version 1.14