Coverage Report

Created: 2026-03-30 09:00

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