Coverage Report

Created: 2025-06-09 08:44

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