Line data Source code
1 : /*******************************************************************************
2 : * Project: OGR CAD Driver
3 : * Purpose: Implements driver based on libopencad
4 : * Author: Alexandr Borzykh, mush3d at gmail.com
5 : * Author: Dmitry Baryshnikov, polimax@mail.ru
6 : * Language: C++
7 : *******************************************************************************
8 : * The MIT License (MIT)
9 : *
10 : * Copyright (c) 2016 Alexandr Borzykh
11 : * Copyright (c) 2016-2019, NextGIS <info@nextgis.com>
12 : *
13 : * 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 : #define FIELD_NAME_GEOMTYPE "cadgeom_type"
23 : #define FIELD_NAME_THICKNESS "thickness"
24 : #define FIELD_NAME_COLOR "color"
25 : #define FIELD_NAME_EXT_DATA "extentity_data"
26 : #define FIELD_NAME_TEXT "text"
27 :
28 : constexpr double DEG2RAD = M_PI / 180.0;
29 : constexpr double RAD2DEG = 1.0 / DEG2RAD;
30 :
31 10 : OGRCADLayer::OGRCADLayer(GDALDataset *poDS, CADLayer &poCADLayer_,
32 10 : OGRSpatialReference *poSR, int nEncoding)
33 : : m_poDS(poDS), poSpatialRef(poSR), poCADLayer(poCADLayer_),
34 10 : nDWGEncoding(nEncoding)
35 : {
36 10 : nNextFID = 0;
37 :
38 10 : if (poSpatialRef)
39 10 : poSpatialRef->Reference();
40 10 : poFeatureDefn =
41 10 : new OGRFeatureDefn(CADRecode(poCADLayer_.getName(), nDWGEncoding));
42 :
43 : // Setting up layer geometry type
44 : OGRwkbGeometryType eGeomType;
45 10 : char dLineStringPresented = 0;
46 10 : char dCircularStringPresented = 0;
47 10 : char dPointPresented = 0;
48 10 : char dPolygonPresented = 0;
49 : std::vector<CADObject::ObjectType> aePresentedGeometryTypes =
50 20 : poCADLayer.getGeometryTypes();
51 28 : for (size_t i = 0; i < aePresentedGeometryTypes.size(); ++i)
52 : {
53 18 : switch (aePresentedGeometryTypes[i])
54 : {
55 13 : case CADObject::ATTDEF:
56 : case CADObject::TEXT:
57 : case CADObject::MTEXT:
58 : case CADObject::POINT:
59 13 : dPointPresented = 1;
60 13 : break;
61 3 : case CADObject::CIRCLE:
62 3 : dCircularStringPresented = 1;
63 3 : break;
64 2 : case CADObject::SPLINE:
65 : case CADObject::ELLIPSE:
66 : case CADObject::ARC:
67 : case CADObject::POLYLINE3D:
68 : case CADObject::POLYLINE2D:
69 : case CADObject::LWPOLYLINE:
70 : case CADObject::LINE:
71 2 : dLineStringPresented = 1;
72 2 : break;
73 0 : case CADObject::FACE3D:
74 : case CADObject::SOLID:
75 0 : dPolygonPresented = 1;
76 0 : break;
77 0 : default:
78 0 : break;
79 : }
80 : }
81 :
82 10 : if ((dLineStringPresented + dCircularStringPresented + dPointPresented +
83 10 : dPolygonPresented) > 1)
84 : {
85 0 : eGeomType = wkbGeometryCollection;
86 : }
87 : else
88 : {
89 10 : if (dLineStringPresented)
90 : {
91 2 : eGeomType = wkbLineString;
92 : }
93 8 : else if (dCircularStringPresented)
94 : {
95 3 : eGeomType = wkbCircularString;
96 : }
97 5 : else if (dPointPresented)
98 : {
99 5 : eGeomType = wkbPoint;
100 : }
101 0 : else if (dPolygonPresented)
102 : {
103 0 : eGeomType = wkbPolygon;
104 : }
105 : else
106 : {
107 0 : eGeomType = wkbUnknown;
108 : }
109 : }
110 10 : poFeatureDefn->SetGeomType(eGeomType);
111 :
112 20 : OGRFieldDefn oClassField(FIELD_NAME_GEOMTYPE, OFTString);
113 10 : poFeatureDefn->AddFieldDefn(&oClassField);
114 :
115 20 : OGRFieldDefn oLinetypeField(FIELD_NAME_THICKNESS, OFTReal);
116 10 : poFeatureDefn->AddFieldDefn(&oLinetypeField);
117 :
118 20 : OGRFieldDefn oColorField(FIELD_NAME_COLOR, OFTString);
119 10 : poFeatureDefn->AddFieldDefn(&oColorField);
120 :
121 20 : OGRFieldDefn oExtendedField(FIELD_NAME_EXT_DATA, OFTString);
122 10 : poFeatureDefn->AddFieldDefn(&oExtendedField);
123 :
124 20 : OGRFieldDefn oTextField(FIELD_NAME_TEXT, OFTString);
125 10 : poFeatureDefn->AddFieldDefn(&oTextField);
126 :
127 20 : auto oAttrTags = poCADLayer.getAttributesTags();
128 14 : for (const std::string &osTag : oAttrTags)
129 : {
130 4 : auto ret = asFeaturesAttributes.insert(osTag);
131 4 : if (ret.second == true)
132 : {
133 8 : OGRFieldDefn oAttrField(osTag.c_str(), OFTString);
134 4 : poFeatureDefn->AddFieldDefn(&oAttrField);
135 : }
136 : }
137 :
138 : // Applying spatial ref info
139 10 : if (poFeatureDefn->GetGeomFieldCount() != 0)
140 10 : poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSpatialRef);
141 :
142 10 : SetDescription(poFeatureDefn->GetName());
143 10 : poFeatureDefn->Reference();
144 10 : }
145 :
146 7 : GIntBig OGRCADLayer::GetFeatureCount(int bForce)
147 : {
148 7 : if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
149 0 : return OGRLayer::GetFeatureCount(bForce);
150 :
151 7 : return poCADLayer.getGeometryCount();
152 : }
153 :
154 0 : int OGRCADLayer::TestCapability(const char *pszCap)
155 : {
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 : }
165 :
166 20 : OGRCADLayer::~OGRCADLayer()
167 : {
168 10 : if (poSpatialRef)
169 10 : poSpatialRef->Release();
170 10 : poFeatureDefn->Release();
171 20 : }
172 :
173 4 : void OGRCADLayer::ResetReading()
174 : {
175 4 : nNextFID = 0;
176 4 : }
177 :
178 13 : OGRFeature *OGRCADLayer::GetNextFeature()
179 : {
180 13 : OGRFeature *poFeature = GetFeature(nNextFID);
181 13 : ++nNextFID;
182 :
183 13 : if (poFeature == nullptr)
184 0 : return nullptr;
185 :
186 26 : if ((m_poFilterGeom == nullptr ||
187 26 : FilterGeometry(poFeature->GetGeometryRef())) &&
188 13 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
189 : {
190 13 : return poFeature;
191 : }
192 :
193 0 : return nullptr;
194 : }
195 :
196 13 : OGRFeature *OGRCADLayer::GetFeature(GIntBig nFID)
197 : {
198 13 : if (poCADLayer.getGeometryCount() <= static_cast<size_t>(nFID) || nFID < 0)
199 : {
200 0 : return nullptr;
201 : }
202 :
203 13 : OGRFeature *poFeature = nullptr;
204 : CADGeometry *poCADGeometry =
205 13 : poCADLayer.getGeometry(static_cast<size_t>(nFID));
206 :
207 26 : if (nullptr == poCADGeometry ||
208 13 : GetLastErrorCode() != CADErrorCodes::SUCCESS)
209 : {
210 0 : CPLError(CE_Failure, CPLE_NotSupported,
211 : "Failed to get geometry with ID = " CPL_FRMT_GIB
212 : " from layer \"%s\". Libopencad errorcode: %d",
213 0 : nFID, poCADLayer.getName().c_str(), GetLastErrorCode());
214 0 : return nullptr;
215 : }
216 :
217 13 : poFeature = new OGRFeature(poFeatureDefn);
218 13 : poFeature->SetFID(nFID);
219 13 : poFeature->SetField(FIELD_NAME_THICKNESS, poCADGeometry->getThickness());
220 :
221 13 : if (!poCADGeometry->getEED().empty())
222 : {
223 6 : std::vector<std::string> asGeometryEED = poCADGeometry->getEED();
224 6 : std::string sEEDAsOneString = "";
225 3 : for (std::vector<std::string>::const_iterator iter =
226 3 : asGeometryEED.cbegin();
227 9 : iter != asGeometryEED.cend(); ++iter)
228 : {
229 3 : sEEDAsOneString += *iter;
230 3 : sEEDAsOneString += ' ';
231 : }
232 :
233 3 : poFeature->SetField(FIELD_NAME_EXT_DATA, sEEDAsOneString.c_str());
234 : }
235 :
236 13 : RGBColor stRGB = poCADGeometry->getColor();
237 26 : CPLString sHexColor;
238 13 : sHexColor.Printf("#%02X%02X%02X%02X", stRGB.R, stRGB.G, stRGB.B, 255);
239 13 : poFeature->SetField(FIELD_NAME_COLOR, sHexColor);
240 :
241 26 : CPLString sStyle;
242 13 : sStyle.Printf("PEN(c:%s,w:5px)", sHexColor.c_str());
243 13 : poFeature->SetStyleString(sStyle);
244 :
245 26 : std::vector<CADAttrib> oBlockAttrs = poCADGeometry->getBlockAttributes();
246 13 : for (const CADAttrib &oAttrib : oBlockAttrs)
247 : {
248 0 : CPLString osTag = oAttrib.getTag();
249 0 : auto featureAttrIt = asFeaturesAttributes.find(osTag);
250 0 : if (featureAttrIt != asFeaturesAttributes.end())
251 : {
252 0 : poFeature->SetField(*featureAttrIt, oAttrib.getTextValue().c_str());
253 : }
254 : }
255 :
256 13 : switch (poCADGeometry->getType())
257 : {
258 1 : case CADGeometry::POINT:
259 : {
260 : CADPoint3D *const poCADPoint =
261 1 : cpl::down_cast<CADPoint3D *>(poCADGeometry);
262 1 : CADVector stPositionVector = poCADPoint->getPosition();
263 :
264 1 : poFeature->SetGeometryDirectly(
265 1 : new OGRPoint(stPositionVector.getX(), stPositionVector.getY(),
266 1 : stPositionVector.getZ()));
267 1 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADPoint");
268 1 : break;
269 : }
270 :
271 1 : case CADGeometry::LINE:
272 : {
273 1 : CADLine *const poCADLine = cpl::down_cast<CADLine *>(poCADGeometry);
274 1 : OGRLineString *poLS = new OGRLineString();
275 2 : poLS->addPoint(poCADLine->getStart().getPosition().getX(),
276 2 : poCADLine->getStart().getPosition().getY(),
277 2 : poCADLine->getStart().getPosition().getZ());
278 2 : poLS->addPoint(poCADLine->getEnd().getPosition().getX(),
279 2 : poCADLine->getEnd().getPosition().getY(),
280 2 : poCADLine->getEnd().getPosition().getZ());
281 :
282 1 : poFeature->SetGeometryDirectly(poLS);
283 1 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADLine");
284 1 : break;
285 : }
286 :
287 0 : case CADGeometry::SOLID:
288 : {
289 : 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 : {
297 0 : poLR->addPoint(astSolidCorners[i].getX(),
298 0 : astSolidCorners[i].getY(),
299 0 : astSolidCorners[i].getZ());
300 : }
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 : }
308 :
309 3 : case CADGeometry::CIRCLE:
310 : {
311 3 : CADCircle *poCADCircle = cpl::down_cast<CADCircle *>(poCADGeometry);
312 3 : OGRCircularString *poCircle = new OGRCircularString();
313 :
314 3 : CADVector stCircleCenter = poCADCircle->getPosition();
315 6 : OGRPoint oCirclePoint1;
316 3 : oCirclePoint1.setX(stCircleCenter.getX() -
317 3 : poCADCircle->getRadius());
318 3 : oCirclePoint1.setY(stCircleCenter.getY());
319 3 : oCirclePoint1.setZ(stCircleCenter.getZ());
320 3 : poCircle->addPoint(&oCirclePoint1);
321 :
322 6 : OGRPoint oCirclePoint2;
323 3 : oCirclePoint2.setX(stCircleCenter.getX());
324 3 : oCirclePoint2.setY(stCircleCenter.getY() +
325 3 : poCADCircle->getRadius());
326 3 : oCirclePoint2.setZ(stCircleCenter.getZ());
327 3 : poCircle->addPoint(&oCirclePoint2);
328 :
329 6 : OGRPoint oCirclePoint3;
330 3 : oCirclePoint3.setX(stCircleCenter.getX() +
331 3 : poCADCircle->getRadius());
332 3 : oCirclePoint3.setY(stCircleCenter.getY());
333 3 : oCirclePoint3.setZ(stCircleCenter.getZ());
334 3 : poCircle->addPoint(&oCirclePoint3);
335 :
336 6 : OGRPoint oCirclePoint4;
337 3 : oCirclePoint4.setX(stCircleCenter.getX());
338 3 : oCirclePoint4.setY(stCircleCenter.getY() -
339 3 : poCADCircle->getRadius());
340 3 : oCirclePoint4.setZ(stCircleCenter.getZ());
341 3 : poCircle->addPoint(&oCirclePoint4);
342 :
343 : // Close the circle
344 3 : 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 3 : poFeature->SetGeometryDirectly(poCircle);
357 :
358 3 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADCircle");
359 3 : break;
360 : }
361 :
362 0 : case CADGeometry::ARC:
363 : {
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 : }
413 :
414 0 : case CADGeometry::FACE3D:
415 : {
416 : 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 : {
423 0 : poLR->addPoint(poCADFace->getCorner(i).getX(),
424 0 : poCADFace->getCorner(i).getY(),
425 0 : poCADFace->getCorner(i).getZ());
426 : }
427 0 : if (!(poCADFace->getCorner(2) == poCADFace->getCorner(3)))
428 : {
429 0 : poLR->addPoint(poCADFace->getCorner(3).getX(),
430 0 : poCADFace->getCorner(3).getY(),
431 0 : poCADFace->getCorner(3).getZ());
432 : }
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 : }
440 :
441 0 : case CADGeometry::LWPOLYLINE:
442 : {
443 : 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 : {
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 : }
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 : {
470 0 : for (size_t i = 0; i < poCADLWPolyline->getVertexCount(); ++i)
471 : {
472 0 : CADVector stVertex = poCADLWPolyline->getVertex(i);
473 0 : poLS->addPoint(stVertex.getX(), stVertex.getY(),
474 : stVertex.getZ());
475 : }
476 :
477 0 : poFeature->SetGeometryDirectly(poLS);
478 0 : break;
479 : }
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 : const size_t nCount =
487 0 : std::min(adfBulges.size(), poCADLWPolyline->getVertexCount());
488 :
489 0 : for (size_t iCurrentVertex = 0; iCurrentVertex + 1 < nCount;
490 : iCurrentVertex++)
491 : {
492 : CADVector stCurrentVertex =
493 0 : poCADLWPolyline->getVertex(iCurrentVertex);
494 : CADVector stNextVertex =
495 0 : poCADLWPolyline->getVertex(iCurrentVertex + 1);
496 :
497 : 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 : {
506 0 : if (!bLineStringStarted)
507 : {
508 0 : poLS->addPoint(stCurrentVertex.getX(),
509 : stCurrentVertex.getY(),
510 : stCurrentVertex.getZ());
511 0 : bLineStringStarted = true;
512 : }
513 :
514 0 : poLS->addPoint(stNextVertex.getX(), stNextVertex.getY(),
515 : stNextVertex.getZ());
516 : }
517 : else
518 : {
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 : 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 : double dfLineDir =
575 0 : stNextVertex.getY() > stCurrentVertex.getY() ? 1.0f
576 0 : : -1.0f;
577 :
578 : /*
579 : * Get arc's starting angle.
580 : */
581 : 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 : 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 : OGRLineString *poArcpoLS =
620 : OGRGeometryFactory::approximateArcAngles(
621 : stOgrArcCenter.getX(), stOgrArcCenter.getY(),
622 : stOgrArcCenter.getZ(), dfOgrArcRadius,
623 : dfOgrArcRadius, dfOgrArcRotation,
624 : dfOgrArcStartAngle, dfOgrArcEndAngle, 0.0)
625 0 : ->toLineString();
626 :
627 0 : poLS->addSubLineString(poArcpoLS);
628 :
629 0 : delete (poArcpoLS);
630 : }
631 : }
632 :
633 0 : if (poCADLWPolyline->isClosed())
634 : {
635 0 : poLS->addPoint(poCADLWPolyline->getVertex(0).getX(),
636 0 : poCADLWPolyline->getVertex(0).getY(),
637 0 : poCADLWPolyline->getVertex(0).getZ());
638 : }
639 :
640 0 : poFeature->SetGeometryDirectly(poLS);
641 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADLWPolyline");
642 0 : break;
643 : }
644 :
645 : // TODO: Unsupported smooth lines
646 0 : case CADGeometry::POLYLINE3D:
647 : {
648 : 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 : {
654 0 : CADVector stVertex = poCADPolyline3D->getVertex(i);
655 :
656 0 : poLS->addPoint(stVertex.getX(), stVertex.getY(),
657 : stVertex.getZ());
658 : }
659 :
660 0 : poFeature->SetGeometryDirectly(poLS);
661 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADPolyline3D");
662 0 : break;
663 : }
664 :
665 4 : case CADGeometry::TEXT:
666 : {
667 4 : CADText *const poCADText = cpl::down_cast<CADText *>(poCADGeometry);
668 4 : OGRPoint *poPoint = new OGRPoint(poCADText->getPosition().getX(),
669 4 : poCADText->getPosition().getY(),
670 4 : poCADText->getPosition().getZ());
671 : CPLString sTextValue =
672 12 : CADRecode(poCADText->getTextValue(), nDWGEncoding);
673 :
674 4 : poFeature->SetField(FIELD_NAME_TEXT, sTextValue);
675 4 : poFeature->SetGeometryDirectly(poPoint);
676 4 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADText");
677 :
678 : sStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",c:%s)",
679 4 : sTextValue.c_str(), sHexColor.c_str());
680 4 : poFeature->SetStyleString(sStyle);
681 4 : break;
682 : }
683 :
684 2 : case CADGeometry::MTEXT:
685 : {
686 : CADMText *const poCADMText =
687 2 : cpl::down_cast<CADMText *>(poCADGeometry);
688 2 : OGRPoint *poPoint = new OGRPoint(poCADMText->getPosition().getX(),
689 2 : poCADMText->getPosition().getY(),
690 2 : poCADMText->getPosition().getZ());
691 : CPLString sTextValue =
692 6 : CADRecode(poCADMText->getTextValue(), nDWGEncoding);
693 :
694 2 : poFeature->SetField(FIELD_NAME_TEXT, sTextValue);
695 2 : poFeature->SetGeometryDirectly(poPoint);
696 2 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADMText");
697 :
698 : sStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",c:%s)",
699 2 : sTextValue.c_str(), sHexColor.c_str());
700 2 : poFeature->SetStyleString(sStyle);
701 2 : break;
702 : }
703 :
704 0 : case CADGeometry::SPLINE:
705 : {
706 : 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 : {
713 0 : poLS->addPoint(poCADSpline->getControlPoints()[i].getX(),
714 0 : poCADSpline->getControlPoints()[i].getY(),
715 0 : poCADSpline->getControlPoints()[i].getZ());
716 : }
717 :
718 0 : poFeature->SetGeometryDirectly(poLS);
719 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADSpline");
720 0 : break;
721 : }
722 :
723 1 : case CADGeometry::ELLIPSE:
724 : {
725 : CADEllipse *poCADEllipse =
726 1 : 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 1 : double dfStartAngle = poCADEllipse->getStartingAngle() * RAD2DEG;
731 1 : double dfEndAngle = poCADEllipse->getEndingAngle() * RAD2DEG;
732 1 : if (dfStartAngle > dfEndAngle)
733 : {
734 0 : dfEndAngle += 360.0;
735 : }
736 1 : double dfAxisRatio = poCADEllipse->getAxisRatio();
737 :
738 1 : CADVector stEllipseCenter = poCADEllipse->getPosition();
739 1 : CADVector vectSMAxis = poCADEllipse->getSMAxis();
740 : double dfPrimaryRadius, dfSecondaryRadius;
741 : double dfRotation;
742 1 : dfPrimaryRadius = sqrt(vectSMAxis.getX() * vectSMAxis.getX() +
743 1 : vectSMAxis.getY() * vectSMAxis.getY() +
744 1 : vectSMAxis.getZ() * vectSMAxis.getZ());
745 :
746 1 : dfSecondaryRadius = dfAxisRatio * dfPrimaryRadius;
747 :
748 1 : dfRotation =
749 1 : -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 1 : CPLDebug("CAD",
789 : "Position: %f, %f, %f, radius %f/%f, start angle: %f, end "
790 : "angle: %f, rotation: %f",
791 : stEllipseCenter.getX(), stEllipseCenter.getY(),
792 : stEllipseCenter.getZ(), dfPrimaryRadius, dfSecondaryRadius,
793 : dfStartAngle, dfEndAngle, dfRotation);
794 1 : OGRGeometry *poEllipse = OGRGeometryFactory::approximateArcAngles(
795 : stEllipseCenter.getX(), stEllipseCenter.getY(),
796 : stEllipseCenter.getZ(), dfPrimaryRadius, dfSecondaryRadius,
797 : dfRotation, dfStartAngle, dfEndAngle, 0.0);
798 :
799 1 : poFeature->SetGeometryDirectly(poEllipse);
800 1 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADEllipse");
801 1 : break;
802 : }
803 :
804 1 : case CADGeometry::ATTDEF:
805 : {
806 : CADAttdef *const poCADAttdef =
807 1 : cpl::down_cast<CADAttdef *>(poCADGeometry);
808 1 : OGRPoint *poPoint = new OGRPoint(poCADAttdef->getPosition().getX(),
809 1 : poCADAttdef->getPosition().getY(),
810 1 : poCADAttdef->getPosition().getZ());
811 : CPLString sTextValue =
812 3 : CADRecode(poCADAttdef->getTag(), nDWGEncoding);
813 :
814 1 : poFeature->SetField(FIELD_NAME_TEXT, sTextValue);
815 1 : poFeature->SetGeometryDirectly(poPoint);
816 1 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADAttdef");
817 :
818 : sStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",c:%s)",
819 1 : sTextValue.c_str(), sHexColor.c_str());
820 1 : poFeature->SetStyleString(sStyle);
821 1 : break;
822 : }
823 :
824 0 : default:
825 : {
826 0 : CPLError(CE_Warning, CPLE_NotSupported,
827 : "Unhandled feature. Skipping it.");
828 :
829 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADUnknown");
830 0 : delete poCADGeometry;
831 0 : return poFeature;
832 : }
833 : }
834 :
835 13 : delete poCADGeometry;
836 13 : poFeature->GetGeometryRef()->assignSpatialReference(poSpatialRef);
837 13 : return poFeature;
838 : }
|