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