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 1 : CADPoint3D *const poCADPoint = (CADPoint3D *)poCADGeometry;
261 1 : CADVector stPositionVector = poCADPoint->getPosition();
262 :
263 1 : poFeature->SetGeometryDirectly(
264 1 : new OGRPoint(stPositionVector.getX(), stPositionVector.getY(),
265 1 : stPositionVector.getZ()));
266 1 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADPoint");
267 1 : break;
268 : }
269 :
270 1 : case CADGeometry::LINE:
271 : {
272 1 : CADLine *const poCADLine = (CADLine *)poCADGeometry;
273 1 : OGRLineString *poLS = new OGRLineString();
274 2 : poLS->addPoint(poCADLine->getStart().getPosition().getX(),
275 2 : poCADLine->getStart().getPosition().getY(),
276 2 : poCADLine->getStart().getPosition().getZ());
277 2 : poLS->addPoint(poCADLine->getEnd().getPosition().getX(),
278 2 : poCADLine->getEnd().getPosition().getY(),
279 2 : poCADLine->getEnd().getPosition().getZ());
280 :
281 1 : poFeature->SetGeometryDirectly(poLS);
282 1 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADLine");
283 1 : break;
284 : }
285 :
286 0 : case CADGeometry::SOLID:
287 : {
288 0 : CADSolid *const poCADSolid = (CADSolid *)poCADGeometry;
289 0 : OGRPolygon *poPoly = new OGRPolygon();
290 0 : OGRLinearRing *poLR = new OGRLinearRing();
291 :
292 0 : std::vector<CADVector> astSolidCorners = poCADSolid->getCorners();
293 0 : for (size_t i = 0; i < astSolidCorners.size(); ++i)
294 : {
295 0 : poLR->addPoint(astSolidCorners[i].getX(),
296 0 : astSolidCorners[i].getY(),
297 0 : astSolidCorners[i].getZ());
298 : }
299 0 : poPoly->addRingDirectly(poLR);
300 0 : poPoly->closeRings();
301 0 : poFeature->SetGeometryDirectly(poPoly);
302 :
303 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADSolid");
304 0 : break;
305 : }
306 :
307 3 : case CADGeometry::CIRCLE:
308 : {
309 3 : CADCircle *poCADCircle = static_cast<CADCircle *>(poCADGeometry);
310 3 : OGRCircularString *poCircle = new OGRCircularString();
311 :
312 3 : CADVector stCircleCenter = poCADCircle->getPosition();
313 6 : OGRPoint oCirclePoint1;
314 3 : oCirclePoint1.setX(stCircleCenter.getX() -
315 3 : poCADCircle->getRadius());
316 3 : oCirclePoint1.setY(stCircleCenter.getY());
317 3 : oCirclePoint1.setZ(stCircleCenter.getZ());
318 3 : poCircle->addPoint(&oCirclePoint1);
319 :
320 6 : OGRPoint oCirclePoint2;
321 3 : oCirclePoint2.setX(stCircleCenter.getX());
322 3 : oCirclePoint2.setY(stCircleCenter.getY() +
323 3 : poCADCircle->getRadius());
324 3 : oCirclePoint2.setZ(stCircleCenter.getZ());
325 3 : poCircle->addPoint(&oCirclePoint2);
326 :
327 6 : OGRPoint oCirclePoint3;
328 3 : oCirclePoint3.setX(stCircleCenter.getX() +
329 3 : poCADCircle->getRadius());
330 3 : oCirclePoint3.setY(stCircleCenter.getY());
331 3 : oCirclePoint3.setZ(stCircleCenter.getZ());
332 3 : poCircle->addPoint(&oCirclePoint3);
333 :
334 6 : OGRPoint oCirclePoint4;
335 3 : oCirclePoint4.setX(stCircleCenter.getX());
336 3 : oCirclePoint4.setY(stCircleCenter.getY() -
337 3 : poCADCircle->getRadius());
338 3 : oCirclePoint4.setZ(stCircleCenter.getZ());
339 3 : poCircle->addPoint(&oCirclePoint4);
340 :
341 : // Close the circle
342 3 : poCircle->addPoint(&oCirclePoint1);
343 :
344 : /*NOTE: The alternative way:
345 : OGRGeometry *poCircle =
346 : OGRGeometryFactory::approximateArcAngles(
347 : poCADCircle->getPosition().getX(),
348 : poCADCircle->getPosition().getY(),
349 : poCADCircle->getPosition().getZ(),
350 : poCADCircle->getRadius(), poCADCircle->getRadius(), 0.0,
351 : 0.0, 360.0,
352 : 0.0 );
353 : */
354 3 : poFeature->SetGeometryDirectly(poCircle);
355 :
356 3 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADCircle");
357 3 : break;
358 : }
359 :
360 0 : case CADGeometry::ARC:
361 : {
362 0 : CADArc *poCADArc = static_cast<CADArc *>(poCADGeometry);
363 0 : OGRCircularString *poCircle = new OGRCircularString();
364 :
365 : // Need at least 3 points in arc
366 0 : double dfStartAngle = poCADArc->getStartingAngle() * RAD2DEG;
367 0 : double dfEndAngle = poCADArc->getEndingAngle() * RAD2DEG;
368 0 : double dfMidAngle = (dfEndAngle + dfStartAngle) / 2;
369 0 : CADVector stCircleCenter = poCADArc->getPosition();
370 :
371 0 : OGRPoint oCirclePoint;
372 0 : oCirclePoint.setX(stCircleCenter.getX() +
373 0 : poCADArc->getRadius() * cos(dfStartAngle));
374 0 : oCirclePoint.setY(stCircleCenter.getY() +
375 0 : poCADArc->getRadius() * sin(dfStartAngle));
376 0 : oCirclePoint.setZ(stCircleCenter.getZ());
377 0 : poCircle->addPoint(&oCirclePoint);
378 :
379 0 : oCirclePoint.setX(stCircleCenter.getX() +
380 0 : poCADArc->getRadius() * cos(dfMidAngle));
381 0 : oCirclePoint.setY(stCircleCenter.getY() +
382 0 : poCADArc->getRadius() * sin(dfMidAngle));
383 0 : oCirclePoint.setZ(stCircleCenter.getZ());
384 0 : poCircle->addPoint(&oCirclePoint);
385 :
386 0 : oCirclePoint.setX(stCircleCenter.getX() +
387 0 : poCADArc->getRadius() * cos(dfEndAngle));
388 0 : oCirclePoint.setY(stCircleCenter.getY() +
389 0 : poCADArc->getRadius() * sin(dfEndAngle));
390 0 : oCirclePoint.setZ(stCircleCenter.getZ());
391 0 : poCircle->addPoint(&oCirclePoint);
392 :
393 : /*NOTE: alternative way:
394 : OGRGeometry * poArc = OGRGeometryFactory::approximateArcAngles(
395 : poCADArc->getPosition().getX(),
396 : poCADArc->getPosition().getY(),
397 : poCADArc->getPosition().getZ(),
398 : poCADArc->getRadius(), poCADArc->getRadius(), 0.0,
399 : dfStartAngle,
400 : dfStartAngle > dfEndAngle ?
401 : ( dfEndAngle + 360.0f ) :
402 : dfEndAngle,
403 : 0.0 );
404 : */
405 :
406 0 : poFeature->SetGeometryDirectly(poCircle);
407 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADArc");
408 :
409 0 : break;
410 : }
411 :
412 0 : case CADGeometry::FACE3D:
413 : {
414 0 : CADFace3D *const poCADFace = (CADFace3D *)poCADGeometry;
415 0 : OGRPolygon *poPoly = new OGRPolygon();
416 0 : OGRLinearRing *poLR = new OGRLinearRing();
417 :
418 0 : for (size_t i = 0; i < 3; ++i)
419 : {
420 0 : poLR->addPoint(poCADFace->getCorner(i).getX(),
421 0 : poCADFace->getCorner(i).getY(),
422 0 : poCADFace->getCorner(i).getZ());
423 : }
424 0 : if (!(poCADFace->getCorner(2) == poCADFace->getCorner(3)))
425 : {
426 0 : poLR->addPoint(poCADFace->getCorner(3).getX(),
427 0 : poCADFace->getCorner(3).getY(),
428 0 : poCADFace->getCorner(3).getZ());
429 : }
430 0 : poPoly->addRingDirectly(poLR);
431 0 : poPoly->closeRings();
432 0 : poFeature->SetGeometryDirectly(poPoly);
433 :
434 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADFace3D");
435 0 : break;
436 : }
437 :
438 0 : case CADGeometry::LWPOLYLINE:
439 : {
440 0 : CADLWPolyline *const poCADLWPolyline =
441 : (CADLWPolyline *)poCADGeometry;
442 :
443 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADLWPolyline");
444 :
445 : /*
446 : * Excessive check, like in DXF driver.
447 : * I tried to make a single-point polyline, but couldn't make it.
448 : * Probably this check should be removed.
449 : */
450 0 : if (poCADLWPolyline->getVertexCount() == 1)
451 : {
452 0 : poFeature->SetGeometryDirectly(
453 0 : new OGRPoint(poCADLWPolyline->getVertex(0).getX(),
454 0 : poCADLWPolyline->getVertex(0).getY(),
455 0 : poCADLWPolyline->getVertex(0).getZ()));
456 :
457 0 : break;
458 : }
459 :
460 : /*
461 : * If polyline has no arcs, handle it in easy way.
462 : */
463 0 : OGRLineString *poLS = new OGRLineString();
464 :
465 0 : if (poCADLWPolyline->getBulges().empty())
466 : {
467 0 : for (size_t i = 0; i < poCADLWPolyline->getVertexCount(); ++i)
468 : {
469 0 : CADVector stVertex = poCADLWPolyline->getVertex(i);
470 0 : poLS->addPoint(stVertex.getX(), stVertex.getY(),
471 : stVertex.getZ());
472 : }
473 :
474 0 : poFeature->SetGeometryDirectly(poLS);
475 0 : break;
476 : }
477 :
478 : /*
479 : * Last case - if polyline has mixed arcs and lines.
480 : */
481 0 : bool bLineStringStarted = false;
482 0 : std::vector<double> adfBulges = poCADLWPolyline->getBulges();
483 : const size_t nCount =
484 0 : std::min(adfBulges.size(), poCADLWPolyline->getVertexCount());
485 :
486 0 : for (size_t iCurrentVertex = 0; iCurrentVertex + 1 < nCount;
487 : iCurrentVertex++)
488 : {
489 : CADVector stCurrentVertex =
490 0 : poCADLWPolyline->getVertex(iCurrentVertex);
491 : CADVector stNextVertex =
492 0 : poCADLWPolyline->getVertex(iCurrentVertex + 1);
493 :
494 : double dfLength =
495 0 : sqrt(pow(stNextVertex.getX() - stCurrentVertex.getX(), 2) +
496 0 : pow(stNextVertex.getY() - stCurrentVertex.getY(), 2));
497 :
498 : /*
499 : * Handling straight polyline segment.
500 : */
501 0 : if ((dfLength == 0) || (adfBulges[iCurrentVertex] == 0))
502 : {
503 0 : if (!bLineStringStarted)
504 : {
505 0 : poLS->addPoint(stCurrentVertex.getX(),
506 : stCurrentVertex.getY(),
507 : stCurrentVertex.getZ());
508 0 : bLineStringStarted = true;
509 : }
510 :
511 0 : poLS->addPoint(stNextVertex.getX(), stNextVertex.getY(),
512 : stNextVertex.getZ());
513 : }
514 : else
515 : {
516 0 : double dfSegmentBulge = adfBulges[iCurrentVertex];
517 0 : double dfH = (dfSegmentBulge * dfLength) / 2;
518 0 : if (dfH == 0.0)
519 0 : dfH = 1.0; // just to avoid a division by zero
520 0 : double dfRadius =
521 0 : (dfH / 2) + (dfLength * dfLength / (8 * dfH));
522 0 : double dfOgrArcRotation = 0,
523 0 : dfOgrArcRadius = fabs(dfRadius);
524 :
525 : /*
526 : * Set arc's direction and keep bulge positive.
527 : */
528 0 : bool bClockwise = (dfSegmentBulge < 0);
529 0 : if (bClockwise)
530 0 : dfSegmentBulge *= -1;
531 :
532 : /*
533 : * Get arc's center point.
534 : */
535 0 : double dfSaggita = fabs(dfSegmentBulge * (dfLength / 2.0));
536 0 : double dfApo = bClockwise ? -(dfOgrArcRadius - dfSaggita)
537 0 : : -(dfSaggita - dfOgrArcRadius);
538 :
539 0 : CADVector stVertex;
540 0 : stVertex.setX(stCurrentVertex.getX() - stNextVertex.getX());
541 0 : stVertex.setY(stCurrentVertex.getY() - stNextVertex.getY());
542 0 : stVertex.setZ(stCurrentVertex.getZ());
543 :
544 0 : CADVector stMidPoint;
545 0 : stMidPoint.setX(stNextVertex.getX() +
546 0 : 0.5 * stVertex.getX());
547 0 : stMidPoint.setY(stNextVertex.getY() +
548 0 : 0.5 * stVertex.getY());
549 0 : stMidPoint.setZ(stVertex.getZ());
550 :
551 0 : CADVector stPperp;
552 0 : stPperp.setX(stVertex.getY());
553 0 : stPperp.setY(-stVertex.getX());
554 : double dfStPperpLength =
555 0 : sqrt(stPperp.getX() * stPperp.getX() +
556 0 : stPperp.getY() * stPperp.getY());
557 : // TODO: Check that length isnot 0
558 0 : stPperp.setX(stPperp.getX() / dfStPperpLength);
559 0 : stPperp.setY(stPperp.getY() / dfStPperpLength);
560 :
561 0 : CADVector stOgrArcCenter;
562 0 : stOgrArcCenter.setX(stMidPoint.getX() +
563 0 : (stPperp.getX() * dfApo));
564 0 : stOgrArcCenter.setY(stMidPoint.getY() +
565 0 : (stPperp.getY() * dfApo));
566 :
567 : /*
568 : * Get the line's general vertical direction ( -1 = down, +1
569 : * = up ).
570 : */
571 : double dfLineDir =
572 0 : stNextVertex.getY() > stCurrentVertex.getY() ? 1.0f
573 0 : : -1.0f;
574 :
575 : /*
576 : * Get arc's starting angle.
577 : */
578 : double dfA =
579 0 : atan2(
580 0 : (stOgrArcCenter.getY() - stCurrentVertex.getY()),
581 0 : (stOgrArcCenter.getX() - stCurrentVertex.getX())) *
582 0 : RAD2DEG;
583 0 : if (bClockwise && (dfLineDir == 1.0))
584 0 : dfA += (dfLineDir * 180.0);
585 :
586 0 : double dfOgrArcStartAngle =
587 0 : dfA > 0.0 ? -(dfA - 180.0) : -(dfA + 180.0);
588 :
589 : /*
590 : * Get arc's ending angle.
591 : */
592 0 : dfA = atan2((stOgrArcCenter.getY() - stNextVertex.getY()),
593 0 : (stOgrArcCenter.getX() - stNextVertex.getX())) *
594 : RAD2DEG;
595 0 : if (bClockwise && (dfLineDir == 1.0))
596 0 : dfA += (dfLineDir * 180.0);
597 :
598 0 : double dfOgrArcEndAngle =
599 0 : dfA > 0.0 ? -(dfA - 180.0) : -(dfA + 180.0);
600 :
601 0 : if (!bClockwise && (dfOgrArcStartAngle < dfOgrArcEndAngle))
602 0 : dfOgrArcEndAngle = -180.0 + (dfLineDir * dfA);
603 :
604 0 : if (bClockwise && (dfOgrArcStartAngle > dfOgrArcEndAngle))
605 0 : dfOgrArcEndAngle += 360.0;
606 :
607 : /*
608 : * Flip arc's rotation if necessary.
609 : */
610 0 : if (bClockwise && (dfLineDir == 1.0))
611 0 : dfOgrArcRotation = dfLineDir * 180.0;
612 :
613 : /*
614 : * Tessellate the arc segment and append to the linestring.
615 : */
616 : OGRLineString *poArcpoLS = (OGRLineString *)
617 0 : OGRGeometryFactory::approximateArcAngles(
618 : stOgrArcCenter.getX(), stOgrArcCenter.getY(),
619 : stOgrArcCenter.getZ(), dfOgrArcRadius,
620 : dfOgrArcRadius, dfOgrArcRotation,
621 : dfOgrArcStartAngle, dfOgrArcEndAngle, 0.0);
622 :
623 0 : poLS->addSubLineString(poArcpoLS);
624 :
625 0 : delete (poArcpoLS);
626 : }
627 : }
628 :
629 0 : if (poCADLWPolyline->isClosed())
630 : {
631 0 : poLS->addPoint(poCADLWPolyline->getVertex(0).getX(),
632 0 : poCADLWPolyline->getVertex(0).getY(),
633 0 : poCADLWPolyline->getVertex(0).getZ());
634 : }
635 :
636 0 : poFeature->SetGeometryDirectly(poLS);
637 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADLWPolyline");
638 0 : break;
639 : }
640 :
641 : // TODO: Unsupported smooth lines
642 0 : case CADGeometry::POLYLINE3D:
643 : {
644 0 : CADPolyline3D *const poCADPolyline3D =
645 : (CADPolyline3D *)poCADGeometry;
646 0 : OGRLineString *poLS = new OGRLineString();
647 :
648 0 : for (size_t i = 0; i < poCADPolyline3D->getVertexCount(); ++i)
649 : {
650 0 : CADVector stVertex = poCADPolyline3D->getVertex(i);
651 :
652 0 : poLS->addPoint(stVertex.getX(), stVertex.getY(),
653 : stVertex.getZ());
654 : }
655 :
656 0 : poFeature->SetGeometryDirectly(poLS);
657 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADPolyline3D");
658 0 : break;
659 : }
660 :
661 4 : case CADGeometry::TEXT:
662 : {
663 4 : CADText *const poCADText = (CADText *)poCADGeometry;
664 4 : OGRPoint *poPoint = new OGRPoint(poCADText->getPosition().getX(),
665 4 : poCADText->getPosition().getY(),
666 4 : poCADText->getPosition().getZ());
667 : CPLString sTextValue =
668 12 : CADRecode(poCADText->getTextValue(), nDWGEncoding);
669 :
670 4 : poFeature->SetField(FIELD_NAME_TEXT, sTextValue);
671 4 : poFeature->SetGeometryDirectly(poPoint);
672 4 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADText");
673 :
674 : sStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",c:%s)",
675 4 : sTextValue.c_str(), sHexColor.c_str());
676 4 : poFeature->SetStyleString(sStyle);
677 4 : break;
678 : }
679 :
680 2 : case CADGeometry::MTEXT:
681 : {
682 2 : CADMText *const poCADMText = (CADMText *)poCADGeometry;
683 2 : OGRPoint *poPoint = new OGRPoint(poCADMText->getPosition().getX(),
684 2 : poCADMText->getPosition().getY(),
685 2 : poCADMText->getPosition().getZ());
686 : CPLString sTextValue =
687 6 : CADRecode(poCADMText->getTextValue(), nDWGEncoding);
688 :
689 2 : poFeature->SetField(FIELD_NAME_TEXT, sTextValue);
690 2 : poFeature->SetGeometryDirectly(poPoint);
691 2 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADMText");
692 :
693 : sStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",c:%s)",
694 2 : sTextValue.c_str(), sHexColor.c_str());
695 2 : poFeature->SetStyleString(sStyle);
696 2 : break;
697 : }
698 :
699 0 : case CADGeometry::SPLINE:
700 : {
701 0 : CADSpline *const poCADSpline = (CADSpline *)poCADGeometry;
702 0 : OGRLineString *poLS = new OGRLineString();
703 :
704 : // TODO: Interpolate spline as points or curves
705 0 : for (size_t i = 0; i < poCADSpline->getControlPoints().size(); ++i)
706 : {
707 0 : poLS->addPoint(poCADSpline->getControlPoints()[i].getX(),
708 0 : poCADSpline->getControlPoints()[i].getY(),
709 0 : poCADSpline->getControlPoints()[i].getZ());
710 : }
711 :
712 0 : poFeature->SetGeometryDirectly(poLS);
713 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADSpline");
714 0 : break;
715 : }
716 :
717 1 : case CADGeometry::ELLIPSE:
718 : {
719 1 : CADEllipse *poCADEllipse = static_cast<CADEllipse *>(poCADGeometry);
720 :
721 : // FIXME: Start/end angles should be swapped to work exactly as DXF
722 : // driver. is it correct?
723 1 : double dfStartAngle = poCADEllipse->getStartingAngle() * RAD2DEG;
724 1 : double dfEndAngle = poCADEllipse->getEndingAngle() * RAD2DEG;
725 1 : if (dfStartAngle > dfEndAngle)
726 : {
727 0 : dfEndAngle += 360.0;
728 : }
729 1 : double dfAxisRatio = poCADEllipse->getAxisRatio();
730 :
731 1 : CADVector stEllipseCenter = poCADEllipse->getPosition();
732 1 : CADVector vectSMAxis = poCADEllipse->getSMAxis();
733 : double dfPrimaryRadius, dfSecondaryRadius;
734 : double dfRotation;
735 1 : dfPrimaryRadius = sqrt(vectSMAxis.getX() * vectSMAxis.getX() +
736 1 : vectSMAxis.getY() * vectSMAxis.getY() +
737 1 : vectSMAxis.getZ() * vectSMAxis.getZ());
738 :
739 1 : dfSecondaryRadius = dfAxisRatio * dfPrimaryRadius;
740 :
741 1 : dfRotation =
742 1 : -1 * atan2(vectSMAxis.getY(), vectSMAxis.getX()) * RAD2DEG;
743 : /* NOTE: alternative way:
744 : OGRCircularString * poEllipse = new OGRCircularString();
745 : OGRPoint oEllipsePoint1;
746 : oEllipsePoint1.setX( stEllipseCenter.getX() - dfPrimaryRadius *
747 : cos( dfRotation ) );
748 : oEllipsePoint1.setY( stEllipseCenter.getY() + dfPrimaryRadius *
749 : sin( dfRotation ) );
750 : oEllipsePoint1.setZ( stEllipseCenter.getZ() );
751 : poEllipse->addPoint( &oEllipsePoint1 );
752 :
753 : OGRPoint oEllipsePoint2;
754 : oEllipsePoint2.setX( stEllipseCenter.getX() + dfSecondaryRadius *
755 : cos( dfRotation ) );
756 : oEllipsePoint2.setY( stEllipseCenter.getY() + dfSecondaryRadius *
757 : sin( dfRotation ) );
758 : oEllipsePoint2.setZ( stEllipseCenter.getZ() );
759 : poEllipse->addPoint( &oEllipsePoint2 );
760 :
761 : OGRPoint oEllipsePoint3;
762 : oEllipsePoint3.setX( stEllipseCenter.getX() + dfPrimaryRadius *
763 : cos( dfRotation ) );
764 : oEllipsePoint3.setY( stEllipseCenter.getY() - dfPrimaryRadius *
765 : sin( dfRotation ) );
766 : oEllipsePoint3.setZ( stEllipseCenter.getZ() );
767 : poEllipse->addPoint( &oEllipsePoint3 );
768 :
769 : OGRPoint oEllipsePoint4;
770 : oEllipsePoint4.setX( stEllipseCenter.getX() - dfSecondaryRadius *
771 : cos( dfRotation ) );
772 : oEllipsePoint4.setY( stEllipseCenter.getY() - dfSecondaryRadius *
773 : sin( dfRotation ) );
774 : oEllipsePoint4.setZ( stEllipseCenter.getZ() );
775 : poEllipse->addPoint( &oEllipsePoint4 );
776 :
777 : // Close the ellipse
778 : poEllipse->addPoint( &oEllipsePoint1 );
779 : */
780 :
781 1 : CPLDebug("CAD",
782 : "Position: %f, %f, %f, radius %f/%f, start angle: %f, end "
783 : "angle: %f, rotation: %f",
784 : stEllipseCenter.getX(), stEllipseCenter.getY(),
785 : stEllipseCenter.getZ(), dfPrimaryRadius, dfSecondaryRadius,
786 : dfStartAngle, dfEndAngle, dfRotation);
787 1 : OGRGeometry *poEllipse = OGRGeometryFactory::approximateArcAngles(
788 : stEllipseCenter.getX(), stEllipseCenter.getY(),
789 : stEllipseCenter.getZ(), dfPrimaryRadius, dfSecondaryRadius,
790 : dfRotation, dfStartAngle, dfEndAngle, 0.0);
791 :
792 1 : poFeature->SetGeometryDirectly(poEllipse);
793 1 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADEllipse");
794 1 : break;
795 : }
796 :
797 1 : case CADGeometry::ATTDEF:
798 : {
799 1 : CADAttdef *const poCADAttdef = (CADAttdef *)poCADGeometry;
800 1 : OGRPoint *poPoint = new OGRPoint(poCADAttdef->getPosition().getX(),
801 1 : poCADAttdef->getPosition().getY(),
802 1 : poCADAttdef->getPosition().getZ());
803 : CPLString sTextValue =
804 3 : CADRecode(poCADAttdef->getTag(), nDWGEncoding);
805 :
806 1 : poFeature->SetField(FIELD_NAME_TEXT, sTextValue);
807 1 : poFeature->SetGeometryDirectly(poPoint);
808 1 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADAttdef");
809 :
810 : sStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",c:%s)",
811 1 : sTextValue.c_str(), sHexColor.c_str());
812 1 : poFeature->SetStyleString(sStyle);
813 1 : break;
814 : }
815 :
816 0 : default:
817 : {
818 0 : CPLError(CE_Warning, CPLE_NotSupported,
819 : "Unhandled feature. Skipping it.");
820 :
821 0 : poFeature->SetField(FIELD_NAME_GEOMTYPE, "CADUnknown");
822 0 : delete poCADGeometry;
823 0 : return poFeature;
824 : }
825 : }
826 :
827 13 : delete poCADGeometry;
828 13 : poFeature->GetGeometryRef()->assignSpatialReference(poSpatialRef);
829 13 : return poFeature;
830 : }
|