Coverage Report

Created: 2026-06-30 08:33

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