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