Coverage Report

Created: 2025-11-15 08:43

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