Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: DXF Translator
4 : * Purpose: Implements OGRDXFWriterLayer - the OGRLayer class used for
5 : * writing a DXF file.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
10 : * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "ogr_dxf.h"
16 : #include "cpl_conv.h"
17 : #include "cpl_string.h"
18 : #include "ogr_featurestyle.h"
19 :
20 : #include <cstdlib>
21 :
22 : /************************************************************************/
23 : /* OGRDXFWriterLayer() */
24 : /************************************************************************/
25 :
26 44 : OGRDXFWriterLayer::OGRDXFWriterLayer(OGRDXFWriterDS *poDSIn, VSILFILE *fpIn)
27 : : fp(fpIn),
28 : poFeatureDefn(nullptr), // TODO(schwehr): Can I move the new here?
29 44 : poDS(poDSIn)
30 : {
31 44 : nNextAutoID = 1;
32 44 : bWriteHatch = CPLTestBool(CPLGetConfigOption("DXF_WRITE_HATCH", "YES"));
33 :
34 44 : poFeatureDefn = new OGRFeatureDefn("entities");
35 44 : poFeatureDefn->Reference();
36 :
37 44 : OGRDXFDataSource::AddStandardFields(poFeatureDefn, ODFM_IncludeBlockFields);
38 44 : }
39 :
40 : /************************************************************************/
41 : /* ~OGRDXFWriterLayer() */
42 : /************************************************************************/
43 :
44 88 : OGRDXFWriterLayer::~OGRDXFWriterLayer()
45 :
46 : {
47 44 : if (poFeatureDefn)
48 44 : poFeatureDefn->Release();
49 88 : }
50 :
51 : /************************************************************************/
52 : /* ResetFP() */
53 : /* */
54 : /* Redirect output. Mostly used for writing block definitions. */
55 : /************************************************************************/
56 :
57 2 : void OGRDXFWriterLayer::ResetFP(VSILFILE *fpNew)
58 :
59 : {
60 2 : fp = fpNew;
61 2 : }
62 :
63 : /************************************************************************/
64 : /* TestCapability() */
65 : /************************************************************************/
66 :
67 88 : int OGRDXFWriterLayer::TestCapability(const char *pszCap)
68 :
69 : {
70 88 : if (EQUAL(pszCap, OLCStringsAsUTF8))
71 0 : return TRUE;
72 88 : else if (EQUAL(pszCap, OLCSequentialWrite))
73 16 : return TRUE;
74 : else
75 72 : return FALSE;
76 : }
77 :
78 : /************************************************************************/
79 : /* CreateField() */
80 : /* */
81 : /* This is really a dummy as our fields are precreated. */
82 : /************************************************************************/
83 :
84 80 : OGRErr OGRDXFWriterLayer::CreateField(const OGRFieldDefn *poField,
85 : int bApproxOK)
86 :
87 : {
88 80 : if (poFeatureDefn->GetFieldIndex(poField->GetNameRef()) >= 0 && bApproxOK)
89 0 : return OGRERR_NONE;
90 80 : if (EQUAL(poField->GetNameRef(), "OGR_STYLE"))
91 : {
92 0 : poFeatureDefn->AddFieldDefn(poField);
93 0 : return OGRERR_NONE;
94 : }
95 :
96 80 : CPLError(CE_Failure, CPLE_AppDefined,
97 : "DXF layer does not support arbitrary field creation, field '%s' "
98 : "not created.",
99 : poField->GetNameRef());
100 :
101 80 : return OGRERR_FAILURE;
102 : }
103 :
104 : /************************************************************************/
105 : /* WriteValue() */
106 : /************************************************************************/
107 :
108 703 : int OGRDXFWriterLayer::WriteValue(int nCode, const char *pszValue)
109 :
110 : {
111 703 : CPLString osLinePair;
112 :
113 703 : osLinePair.Printf("%3d\n", nCode);
114 :
115 703 : if (strlen(pszValue) < 255)
116 703 : osLinePair += pszValue;
117 : else
118 0 : osLinePair.append(pszValue, 255);
119 :
120 703 : osLinePair += "\n";
121 :
122 703 : return VSIFWriteL(osLinePair.c_str(), 1, osLinePair.size(), fp) ==
123 1406 : osLinePair.size();
124 : }
125 :
126 : /************************************************************************/
127 : /* WriteValue() */
128 : /************************************************************************/
129 :
130 355 : int OGRDXFWriterLayer::WriteValue(int nCode, int nValue)
131 :
132 : {
133 355 : CPLString osLinePair;
134 :
135 355 : osLinePair.Printf("%3d\n%d\n", nCode, nValue);
136 :
137 355 : return VSIFWriteL(osLinePair.c_str(), 1, osLinePair.size(), fp) ==
138 710 : osLinePair.size();
139 : }
140 :
141 : /************************************************************************/
142 : /* WriteValue() */
143 : /************************************************************************/
144 :
145 597 : int OGRDXFWriterLayer::WriteValue(int nCode, double dfValue)
146 :
147 : {
148 : char szLinePair[64];
149 :
150 597 : CPLsnprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue);
151 597 : size_t nLen = strlen(szLinePair);
152 :
153 597 : return VSIFWriteL(szLinePair, 1, nLen, fp) == nLen;
154 : }
155 :
156 : /************************************************************************/
157 : /* WriteCore() */
158 : /* */
159 : /* Write core fields common to all sorts of elements. */
160 : /************************************************************************/
161 :
162 167 : OGRErr OGRDXFWriterLayer::WriteCore(OGRFeature *poFeature)
163 :
164 : {
165 : /* -------------------------------------------------------------------- */
166 : /* Write out an entity id. I'm not sure why this is critical, */
167 : /* but it seems that VoloView will just quietly fail to open */
168 : /* dxf files without entity ids set on most/all entities. */
169 : /* Also, for reasons I don't understand these ids seem to have */
170 : /* to start somewhere around 0x50 hex (80 decimal). */
171 : /* -------------------------------------------------------------------- */
172 167 : unsigned int nGotFID = 0;
173 167 : poDS->WriteEntityID(fp, nGotFID, poFeature->GetFID());
174 167 : poFeature->SetFID(nGotFID);
175 :
176 167 : WriteValue(100, "AcDbEntity");
177 :
178 : /* -------------------------------------------------------------------- */
179 : /* For now we assign everything to the default layer - layer */
180 : /* "0" - if there is no layer property on the source features. */
181 : /* -------------------------------------------------------------------- */
182 167 : const char *pszLayer = poFeature->GetFieldAsString("Layer");
183 167 : if (pszLayer == nullptr || strlen(pszLayer) == 0)
184 : {
185 160 : WriteValue(8, "0");
186 : }
187 : else
188 : {
189 14 : CPLString osSanitizedLayer(pszLayer);
190 : // Replaced restricted characters with underscore
191 : // See
192 : // http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
193 7 : const char achForbiddenChars[] = {'<', '>', '/', '\\', '"', ':',
194 : ';', '?', '*', '|', '=', '\''};
195 91 : for (size_t i = 0; i < CPL_ARRAYSIZE(achForbiddenChars); ++i)
196 : {
197 84 : osSanitizedLayer.replaceAll(achForbiddenChars[i], '_');
198 : }
199 :
200 : // also remove newline characters (#15067)
201 7 : osSanitizedLayer.replaceAll("\r\n", "_");
202 7 : osSanitizedLayer.replaceAll('\r', '_');
203 7 : osSanitizedLayer.replaceAll('\n', '_');
204 :
205 : const char *pszExists =
206 7 : poDS->oHeaderDS.LookupLayerProperty(osSanitizedLayer, "Exists");
207 14 : if ((pszExists == nullptr || strlen(pszExists) == 0) &&
208 7 : CSLFindString(poDS->papszLayersToCreate, osSanitizedLayer) == -1)
209 : {
210 6 : poDS->papszLayersToCreate =
211 3 : CSLAddString(poDS->papszLayersToCreate, osSanitizedLayer);
212 : }
213 :
214 7 : WriteValue(8, osSanitizedLayer);
215 : }
216 :
217 167 : return OGRERR_NONE;
218 : }
219 :
220 : /************************************************************************/
221 : /* WriteINSERT() */
222 : /************************************************************************/
223 :
224 7 : OGRErr OGRDXFWriterLayer::WriteINSERT(OGRFeature *poFeature)
225 :
226 : {
227 7 : WriteValue(0, "INSERT");
228 7 : WriteCore(poFeature);
229 7 : WriteValue(100, "AcDbBlockReference");
230 7 : WriteValue(2, poFeature->GetFieldAsString("BlockName"));
231 :
232 : // Write style symbol color
233 7 : OGRStyleTool *poTool = nullptr;
234 14 : OGRStyleMgr oSM;
235 7 : if (poFeature->GetStyleString() != nullptr)
236 : {
237 0 : oSM.InitFromFeature(poFeature);
238 :
239 0 : if (oSM.GetPartCount() > 0)
240 0 : poTool = oSM.GetPart(0);
241 : }
242 7 : if (poTool && poTool->GetType() == OGRSTCSymbol)
243 : {
244 0 : OGRStyleSymbol *poSymbol = (OGRStyleSymbol *)poTool;
245 : GBool bDefault;
246 :
247 0 : if (poSymbol->Color(bDefault) != nullptr && !bDefault)
248 0 : WriteValue(62, ColorStringToDXFColor(poSymbol->Color(bDefault)));
249 : }
250 7 : delete poTool;
251 :
252 : /* -------------------------------------------------------------------- */
253 : /* Write location in OCS. */
254 : /* -------------------------------------------------------------------- */
255 7 : int nCoordCount = 0;
256 : const double *padfCoords =
257 7 : poFeature->GetFieldAsDoubleList("BlockOCSCoords", &nCoordCount);
258 :
259 7 : if (nCoordCount == 3)
260 : {
261 0 : WriteValue(10, padfCoords[0]);
262 0 : WriteValue(20, padfCoords[1]);
263 0 : if (!WriteValue(30, padfCoords[2]))
264 0 : return OGRERR_FAILURE;
265 : }
266 : else
267 : {
268 : // We don't have an OCS; we will just assume that the location of
269 : // the geometry (in WCS) is the correct insertion point.
270 7 : OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
271 :
272 7 : WriteValue(10, poPoint->getX());
273 7 : if (!WriteValue(20, poPoint->getY()))
274 0 : return OGRERR_FAILURE;
275 :
276 7 : if (poPoint->getGeometryType() == wkbPoint25D)
277 : {
278 0 : if (!WriteValue(30, poPoint->getZ()))
279 0 : return OGRERR_FAILURE;
280 : }
281 : }
282 :
283 : /* -------------------------------------------------------------------- */
284 : /* Write scaling. */
285 : /* -------------------------------------------------------------------- */
286 7 : int nScaleCount = 0;
287 : const double *padfScale =
288 7 : poFeature->GetFieldAsDoubleList("BlockScale", &nScaleCount);
289 :
290 7 : if (nScaleCount == 3)
291 : {
292 1 : WriteValue(41, padfScale[0]);
293 1 : WriteValue(42, padfScale[1]);
294 1 : WriteValue(43, padfScale[2]);
295 : }
296 :
297 : /* -------------------------------------------------------------------- */
298 : /* Write rotation. */
299 : /* -------------------------------------------------------------------- */
300 7 : const double dfAngle = poFeature->GetFieldAsDouble("BlockAngle");
301 :
302 7 : if (dfAngle != 0.0)
303 : {
304 1 : WriteValue(50, dfAngle); // degrees
305 : }
306 :
307 : /* -------------------------------------------------------------------- */
308 : /* Write OCS normal vector. */
309 : /* -------------------------------------------------------------------- */
310 7 : int nOCSCount = 0;
311 : const double *padfOCS =
312 7 : poFeature->GetFieldAsDoubleList("BlockOCSNormal", &nOCSCount);
313 :
314 7 : if (nOCSCount == 3)
315 : {
316 0 : WriteValue(210, padfOCS[0]);
317 0 : WriteValue(220, padfOCS[1]);
318 0 : WriteValue(230, padfOCS[2]);
319 : }
320 :
321 7 : return OGRERR_NONE;
322 : }
323 :
324 : /************************************************************************/
325 : /* WritePOINT() */
326 : /************************************************************************/
327 :
328 111 : OGRErr OGRDXFWriterLayer::WritePOINT(OGRFeature *poFeature)
329 :
330 : {
331 111 : WriteValue(0, "POINT");
332 111 : WriteCore(poFeature);
333 111 : WriteValue(100, "AcDbPoint");
334 :
335 : // Write style pen color
336 111 : OGRStyleTool *poTool = nullptr;
337 222 : OGRStyleMgr oSM;
338 111 : if (poFeature->GetStyleString() != nullptr)
339 : {
340 0 : oSM.InitFromFeature(poFeature);
341 :
342 0 : if (oSM.GetPartCount() > 0)
343 0 : poTool = oSM.GetPart(0);
344 : }
345 111 : if (poTool && poTool->GetType() == OGRSTCPen)
346 : {
347 0 : OGRStylePen *poPen = (OGRStylePen *)poTool;
348 : GBool bDefault;
349 :
350 0 : if (poPen->Color(bDefault) != nullptr && !bDefault)
351 0 : WriteValue(62, ColorStringToDXFColor(poPen->Color(bDefault)));
352 : }
353 111 : delete poTool;
354 :
355 111 : OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
356 :
357 111 : WriteValue(10, poPoint->getX());
358 111 : if (!WriteValue(20, poPoint->getY()))
359 0 : return OGRERR_FAILURE;
360 :
361 111 : if (poPoint->getGeometryType() == wkbPoint25D)
362 : {
363 4 : if (!WriteValue(30, poPoint->getZ()))
364 0 : return OGRERR_FAILURE;
365 : }
366 :
367 111 : return OGRERR_NONE;
368 : }
369 :
370 : /************************************************************************/
371 : /* TextEscape() */
372 : /* */
373 : /* Translate UTF8 to Win1252 and escape special characters like */
374 : /* newline and space with DXF style escapes. Note that */
375 : /* non-win1252 unicode characters are translated using the */
376 : /* unicode escape sequence. */
377 : /************************************************************************/
378 :
379 1 : CPLString OGRDXFWriterLayer::TextEscape(const char *pszInput)
380 :
381 : {
382 1 : CPLString osResult;
383 1 : wchar_t *panInput = CPLRecodeToWChar(pszInput, CPL_ENC_UTF8, CPL_ENC_UCS2);
384 31 : for (int i = 0; panInput[i] != 0; i++)
385 : {
386 30 : if (panInput[i] == '\n')
387 : {
388 0 : osResult += "\\P";
389 : }
390 30 : else if (panInput[i] == ' ')
391 : {
392 2 : osResult += "\\~";
393 : }
394 28 : else if (panInput[i] == '\\')
395 : {
396 0 : osResult += "\\\\";
397 : }
398 28 : else if (panInput[i] == '^')
399 : {
400 1 : osResult += "^ ";
401 : }
402 27 : else if (panInput[i] < ' ')
403 : {
404 1 : osResult += '^';
405 1 : osResult += static_cast<char>(panInput[i] + '@');
406 : }
407 26 : else if (panInput[i] > 255)
408 : {
409 0 : CPLString osUnicode;
410 0 : osUnicode.Printf("\\U+%04x", (int)panInput[i]);
411 0 : osResult += osUnicode;
412 : }
413 : else
414 : {
415 26 : osResult += (char)panInput[i];
416 : }
417 : }
418 :
419 1 : CPLFree(panInput);
420 :
421 1 : return osResult;
422 : }
423 :
424 : /************************************************************************/
425 : /* PrepareTextStyleDefinition() */
426 : /************************************************************************/
427 : std::map<CPLString, CPLString>
428 1 : OGRDXFWriterLayer::PrepareTextStyleDefinition(OGRStyleLabel *poLabelTool)
429 : {
430 : GBool bDefault;
431 :
432 1 : std::map<CPLString, CPLString> oTextStyleDef;
433 :
434 : /* -------------------------------------------------------------------- */
435 : /* Fetch the data for this text style. */
436 : /* -------------------------------------------------------------------- */
437 1 : const char *pszFontName = poLabelTool->FontName(bDefault);
438 1 : if (!bDefault)
439 1 : oTextStyleDef["Font"] = pszFontName;
440 :
441 1 : const GBool bBold = poLabelTool->Bold(bDefault);
442 1 : if (!bDefault)
443 1 : oTextStyleDef["Bold"] = bBold ? "1" : "0";
444 :
445 1 : const GBool bItalic = poLabelTool->Italic(bDefault);
446 1 : if (!bDefault)
447 0 : oTextStyleDef["Italic"] = bItalic ? "1" : "0";
448 :
449 1 : const double dfStretch = poLabelTool->Stretch(bDefault);
450 1 : if (!bDefault)
451 : {
452 1 : oTextStyleDef["Width"] = CPLString().Printf("%f", dfStretch / 100.0);
453 : }
454 :
455 2 : return oTextStyleDef;
456 : }
457 :
458 : /************************************************************************/
459 : /* WriteTEXT() */
460 : /************************************************************************/
461 :
462 1 : OGRErr OGRDXFWriterLayer::WriteTEXT(OGRFeature *poFeature)
463 :
464 : {
465 1 : WriteValue(0, "MTEXT");
466 1 : WriteCore(poFeature);
467 1 : WriteValue(100, "AcDbMText");
468 :
469 : /* -------------------------------------------------------------------- */
470 : /* Do we have styling information? */
471 : /* -------------------------------------------------------------------- */
472 1 : OGRStyleTool *poTool = nullptr;
473 2 : OGRStyleMgr oSM;
474 :
475 1 : if (poFeature->GetStyleString() != nullptr)
476 : {
477 1 : oSM.InitFromFeature(poFeature);
478 :
479 1 : if (oSM.GetPartCount() > 0)
480 1 : poTool = oSM.GetPart(0);
481 : }
482 :
483 : /* ==================================================================== */
484 : /* Process the LABEL tool. */
485 : /* ==================================================================== */
486 1 : double dfDx = 0.0;
487 1 : double dfDy = 0.0;
488 :
489 1 : if (poTool && poTool->GetType() == OGRSTCLabel)
490 : {
491 1 : OGRStyleLabel *poLabel = (OGRStyleLabel *)poTool;
492 : GBool bDefault;
493 :
494 : /* --------------------------------------------------------------------
495 : */
496 : /* Color */
497 : /* --------------------------------------------------------------------
498 : */
499 1 : if (poLabel->ForeColor(bDefault) != nullptr && !bDefault)
500 1 : WriteValue(62, ColorStringToDXFColor(poLabel->ForeColor(bDefault)));
501 :
502 : /* --------------------------------------------------------------------
503 : */
504 : /* Angle */
505 : /* --------------------------------------------------------------------
506 : */
507 1 : const double dfAngle = poLabel->Angle(bDefault);
508 :
509 1 : if (!bDefault)
510 1 : WriteValue(50, dfAngle);
511 :
512 : /* --------------------------------------------------------------------
513 : */
514 : /* Height - We need to fetch this in georeferenced units - I'm */
515 : /* doubt the default translation mechanism will be much good. */
516 : /* --------------------------------------------------------------------
517 : */
518 1 : poTool->SetUnit(OGRSTUGround);
519 1 : const double dfHeight = poLabel->Size(bDefault);
520 :
521 1 : if (!bDefault)
522 1 : WriteValue(40, dfHeight);
523 :
524 : /* --------------------------------------------------------------------
525 : */
526 : /* Anchor / Attachment Point */
527 : /* --------------------------------------------------------------------
528 : */
529 1 : const int nAnchor = poLabel->Anchor(bDefault);
530 :
531 1 : if (!bDefault)
532 : {
533 : const static int anAnchorMap[] = {-1, 7, 8, 9, 4, 5, 6,
534 : 1, 2, 3, 7, 8, 9};
535 :
536 0 : if (nAnchor > 0 && nAnchor < 13)
537 0 : WriteValue(71, anAnchorMap[nAnchor]);
538 : }
539 :
540 : /* --------------------------------------------------------------------
541 : */
542 : /* Offset */
543 : /* --------------------------------------------------------------------
544 : */
545 1 : dfDx = poLabel->SpacingX(bDefault);
546 1 : dfDy = poLabel->SpacingY(bDefault);
547 :
548 : /* --------------------------------------------------------------------
549 : */
550 : /* Escape the text, and convert to ISO8859. */
551 : /* --------------------------------------------------------------------
552 : */
553 1 : const char *pszText = poLabel->TextString(bDefault);
554 :
555 1 : if (pszText != nullptr && !bDefault)
556 : {
557 2 : CPLString osEscaped = TextEscape(pszText);
558 1 : while (osEscaped.size() > 250)
559 : {
560 0 : WriteValue(3, osEscaped.substr(0, 250).c_str());
561 0 : osEscaped.erase(0, 250);
562 : }
563 1 : WriteValue(1, osEscaped);
564 : }
565 :
566 : /* --------------------------------------------------------------------
567 : */
568 : /* Store the text style in the map. */
569 : /* --------------------------------------------------------------------
570 : */
571 : std::map<CPLString, CPLString> oTextStyleDef =
572 2 : PrepareTextStyleDefinition(poLabel);
573 2 : CPLString osStyleName;
574 :
575 1 : for (const auto &oPair : oNewTextStyles)
576 : {
577 0 : if (oPair.second == oTextStyleDef)
578 : {
579 0 : osStyleName = oPair.first;
580 0 : break;
581 : }
582 : }
583 :
584 1 : if (osStyleName == "")
585 : {
586 :
587 0 : do
588 : {
589 1 : osStyleName.Printf("AutoTextStyle-%d", nNextAutoID++);
590 1 : } while (poDS->oHeaderDS.TextStyleExists(osStyleName));
591 :
592 1 : oNewTextStyles[osStyleName] = std::move(oTextStyleDef);
593 : }
594 :
595 1 : WriteValue(7, osStyleName);
596 : }
597 :
598 1 : delete poTool;
599 :
600 : /* -------------------------------------------------------------------- */
601 : /* Write the location. */
602 : /* -------------------------------------------------------------------- */
603 1 : OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
604 :
605 1 : WriteValue(10, poPoint->getX() + dfDx);
606 1 : if (!WriteValue(20, poPoint->getY() + dfDy))
607 0 : return OGRERR_FAILURE;
608 :
609 1 : if (poPoint->getGeometryType() == wkbPoint25D)
610 : {
611 1 : if (!WriteValue(30, poPoint->getZ()))
612 0 : return OGRERR_FAILURE;
613 : }
614 :
615 1 : return OGRERR_NONE;
616 : }
617 :
618 : /************************************************************************/
619 : /* PrepareLineTypeDefinition() */
620 : /************************************************************************/
621 : std::vector<double>
622 5 : OGRDXFWriterLayer::PrepareLineTypeDefinition(OGRStylePen *poPen)
623 : {
624 :
625 : /* -------------------------------------------------------------------- */
626 : /* Fetch pattern. */
627 : /* -------------------------------------------------------------------- */
628 : GBool bDefault;
629 5 : const char *pszPattern = poPen->Pattern(bDefault);
630 :
631 5 : if (bDefault || strlen(pszPattern) == 0)
632 0 : return std::vector<double>();
633 :
634 : /* -------------------------------------------------------------------- */
635 : /* Split into pen up / pen down bits. */
636 : /* -------------------------------------------------------------------- */
637 5 : char **papszTokens = CSLTokenizeString(pszPattern);
638 10 : std::vector<double> adfWeightTokens;
639 :
640 15 : for (int i = 0; papszTokens != nullptr && papszTokens[i] != nullptr; i++)
641 : {
642 10 : const char *pszToken = papszTokens[i];
643 20 : CPLString osAmount;
644 20 : CPLString osDXFEntry;
645 :
646 : // Split amount and unit.
647 10 : const char *pszUnit = pszToken; // Used after for.
648 48 : for (; strchr("0123456789.", *pszUnit) != nullptr; pszUnit++)
649 : {
650 : }
651 :
652 10 : osAmount.assign(pszToken, (int)(pszUnit - pszToken));
653 :
654 : // If the unit is other than 'g' we really should be trying to
655 : // do some type of transformation - but what to do? Pretty hard.
656 :
657 : // Even entries are "pen down" represented as positive in DXF.
658 : // "Pen up" entries (gaps) are represented as negative.
659 10 : if (i % 2 == 0)
660 5 : adfWeightTokens.push_back(CPLAtof(osAmount));
661 : else
662 5 : adfWeightTokens.push_back(-CPLAtof(osAmount));
663 : }
664 :
665 5 : CSLDestroy(papszTokens);
666 :
667 5 : return adfWeightTokens;
668 : }
669 :
670 : /************************************************************************/
671 : /* IsLineTypeProportional() */
672 : /************************************************************************/
673 :
674 7 : static double IsLineTypeProportional(const std::vector<double> &adfA,
675 : const std::vector<double> &adfB)
676 : {
677 : // If they are not the same length, they are not the same linetype
678 7 : if (adfA.size() != adfB.size())
679 0 : return 0.0;
680 :
681 : // Determine the proportion of the first elements
682 7 : const double dfRatio = (adfA[0] != 0.0) ? (adfB[0] / adfA[0]) : 0.0;
683 :
684 : // Check if all elements follow this proportionality
685 9 : for (size_t iIndex = 1; iIndex < adfA.size(); iIndex++)
686 7 : if (fabs(adfB[iIndex] - (adfA[iIndex] * dfRatio)) > 1e-6)
687 5 : return 0.0;
688 :
689 2 : return dfRatio;
690 : }
691 :
692 : /************************************************************************/
693 : /* WritePOLYLINE() */
694 : /************************************************************************/
695 :
696 30 : OGRErr OGRDXFWriterLayer::WritePOLYLINE(OGRFeature *poFeature,
697 : const OGRGeometry *poGeom)
698 :
699 : {
700 : /* -------------------------------------------------------------------- */
701 : /* For now we handle multilinestrings by writing a series of */
702 : /* entities. */
703 : /* -------------------------------------------------------------------- */
704 30 : if (poGeom == nullptr)
705 26 : poGeom = poFeature->GetGeometryRef();
706 :
707 30 : if (poGeom->IsEmpty())
708 : {
709 0 : return OGRERR_NONE;
710 : }
711 :
712 60 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ||
713 30 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
714 : {
715 4 : const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
716 4 : OGRErr eErr = OGRERR_NONE;
717 8 : for (auto &&poMember : *poGC)
718 : {
719 4 : eErr = WritePOLYLINE(poFeature, poMember);
720 4 : if (eErr != OGRERR_NONE)
721 0 : break;
722 : }
723 :
724 4 : return eErr;
725 : }
726 :
727 : /* -------------------------------------------------------------------- */
728 : /* Polygons are written with on entity per ring. */
729 : /* -------------------------------------------------------------------- */
730 52 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
731 26 : wkbFlatten(poGeom->getGeometryType()) == wkbTriangle)
732 : {
733 0 : const OGRPolygon *poPoly = poGeom->toPolygon();
734 0 : OGRErr eErr = OGRERR_NONE;
735 0 : for (auto &&poRing : *poPoly)
736 : {
737 0 : eErr = WritePOLYLINE(poFeature, poRing);
738 0 : if (eErr != OGRERR_NONE)
739 0 : break;
740 : }
741 :
742 0 : return eErr;
743 : }
744 :
745 : /* -------------------------------------------------------------------- */
746 : /* Do we now have a geometry we can work with? */
747 : /* -------------------------------------------------------------------- */
748 26 : if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
749 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
750 :
751 26 : const OGRLineString *poLS = poGeom->toLineString();
752 :
753 : /* -------------------------------------------------------------------- */
754 : /* Write as a lightweight polygon, */
755 : /* or as POLYLINE if the line contains different heights */
756 : /* -------------------------------------------------------------------- */
757 26 : int bHasDifferentZ = FALSE;
758 26 : if (poLS->getGeometryType() == wkbLineString25D)
759 : {
760 8 : double z0 = poLS->getZ(0);
761 23 : for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
762 : {
763 16 : if (z0 != poLS->getZ(iVert))
764 : {
765 1 : bHasDifferentZ = TRUE;
766 1 : break;
767 : }
768 : }
769 : }
770 :
771 26 : WriteValue(0, bHasDifferentZ ? "POLYLINE" : "LWPOLYLINE");
772 26 : WriteCore(poFeature);
773 26 : if (bHasDifferentZ)
774 : {
775 1 : WriteValue(100, "AcDb3dPolyline");
776 1 : WriteValue(10, 0.0);
777 1 : WriteValue(20, 0.0);
778 1 : WriteValue(30, 0.0);
779 : }
780 : else
781 25 : WriteValue(100, "AcDbPolyline");
782 26 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
783 0 : WriteValue(70, 1 + (bHasDifferentZ ? 8 : 0));
784 : else
785 26 : WriteValue(70, 0 + (bHasDifferentZ ? 8 : 0));
786 26 : if (!bHasDifferentZ)
787 25 : WriteValue(90, poLS->getNumPoints());
788 : else
789 1 : WriteValue(66, "1"); // Vertex Flag
790 :
791 : /* -------------------------------------------------------------------- */
792 : /* Do we have styling information? */
793 : /* -------------------------------------------------------------------- */
794 26 : OGRStyleTool *poTool = nullptr;
795 52 : OGRStyleMgr oSM;
796 :
797 26 : if (poFeature->GetStyleString() != nullptr)
798 : {
799 5 : oSM.InitFromFeature(poFeature);
800 :
801 5 : if (oSM.GetPartCount() > 0)
802 5 : poTool = oSM.GetPart(0);
803 : }
804 :
805 : /* -------------------------------------------------------------------- */
806 : /* Handle a PEN tool to control drawing color and width. */
807 : /* Perhaps one day also dottedness, etc. */
808 : /* -------------------------------------------------------------------- */
809 26 : if (poTool && poTool->GetType() == OGRSTCPen)
810 : {
811 5 : OGRStylePen *poPen = (OGRStylePen *)poTool;
812 : GBool bDefault;
813 :
814 5 : if (poPen->Color(bDefault) != nullptr && !bDefault)
815 5 : WriteValue(62, ColorStringToDXFColor(poPen->Color(bDefault)));
816 :
817 : // we want to fetch the width in ground units.
818 5 : poPen->SetUnit(OGRSTUGround, 1.0);
819 5 : const double dfWidth = poPen->Width(bDefault);
820 :
821 5 : if (!bDefault)
822 5 : WriteValue(370, (int)floor(dfWidth * 100 + 0.5));
823 : }
824 :
825 : /* -------------------------------------------------------------------- */
826 : /* Do we have a Linetype for the feature? */
827 : /* -------------------------------------------------------------------- */
828 52 : CPLString osLineType = poFeature->GetFieldAsString("Linetype");
829 26 : double dfLineTypeScale = 0.0;
830 :
831 26 : bool bGotLinetype = false;
832 :
833 26 : if (!osLineType.empty())
834 : {
835 : std::vector<double> adfLineType =
836 4 : poDS->oHeaderDS.LookupLineType(osLineType);
837 :
838 2 : if (adfLineType.empty() && oNewLineTypes.count(osLineType) > 0)
839 0 : adfLineType = oNewLineTypes[osLineType];
840 :
841 2 : if (!adfLineType.empty())
842 : {
843 1 : bGotLinetype = true;
844 1 : WriteValue(6, osLineType);
845 :
846 : // If the given linetype is proportional to the linetype data
847 : // in the style string, then apply a linetype scale
848 1 : if (poTool != nullptr && poTool->GetType() == OGRSTCPen)
849 : {
850 : std::vector<double> adfDefinition = PrepareLineTypeDefinition(
851 2 : static_cast<OGRStylePen *>(poTool));
852 :
853 1 : if (!adfDefinition.empty())
854 : {
855 : dfLineTypeScale =
856 1 : IsLineTypeProportional(adfLineType, adfDefinition);
857 :
858 1 : if (dfLineTypeScale != 0.0 &&
859 0 : fabs(dfLineTypeScale - 1.0) > 1e-4)
860 : {
861 0 : WriteValue(48, dfLineTypeScale);
862 : }
863 : }
864 : }
865 : }
866 : }
867 :
868 26 : if (!bGotLinetype && poTool != nullptr && poTool->GetType() == OGRSTCPen)
869 : {
870 : std::vector<double> adfDefinition =
871 8 : PrepareLineTypeDefinition(static_cast<OGRStylePen *>(poTool));
872 :
873 4 : if (!adfDefinition.empty())
874 : {
875 : // Is this definition already created and named?
876 7 : for (const auto &oPair : poDS->oHeaderDS.GetLineTypeTable())
877 : {
878 : dfLineTypeScale =
879 4 : IsLineTypeProportional(oPair.second, adfDefinition);
880 4 : if (dfLineTypeScale != 0.0)
881 : {
882 1 : osLineType = oPair.first;
883 1 : break;
884 : }
885 : }
886 :
887 4 : if (dfLineTypeScale == 0.0)
888 : {
889 4 : for (const auto &oPair : oNewLineTypes)
890 : {
891 : dfLineTypeScale =
892 2 : IsLineTypeProportional(oPair.second, adfDefinition);
893 2 : if (dfLineTypeScale != 0.0)
894 : {
895 1 : osLineType = oPair.first;
896 1 : break;
897 : }
898 : }
899 : }
900 :
901 : // If not, create an automatic name for it.
902 4 : if (osLineType == "")
903 : {
904 1 : dfLineTypeScale = 1.0;
905 0 : do
906 : {
907 1 : osLineType.Printf("AutoLineType-%d", nNextAutoID++);
908 1 : } while (poDS->oHeaderDS.LookupLineType(osLineType).size() > 0);
909 : }
910 :
911 : // If it isn't already defined, add it now.
912 7 : if (poDS->oHeaderDS.LookupLineType(osLineType).empty() &&
913 3 : oNewLineTypes.count(osLineType) == 0)
914 : {
915 2 : oNewLineTypes[osLineType] = std::move(adfDefinition);
916 : }
917 :
918 4 : WriteValue(6, osLineType);
919 :
920 4 : if (dfLineTypeScale != 0.0 && fabs(dfLineTypeScale - 1.0) > 1e-4)
921 : {
922 2 : WriteValue(48, dfLineTypeScale);
923 : }
924 : }
925 : }
926 :
927 : /* -------------------------------------------------------------------- */
928 : /* Write the vertices */
929 : /* -------------------------------------------------------------------- */
930 :
931 26 : if (!bHasDifferentZ && poLS->getGeometryType() == wkbLineString25D)
932 : {
933 : // if LWPOLYLINE with Z write it only once
934 7 : if (!WriteValue(38, poLS->getZ(0)))
935 0 : return OGRERR_FAILURE;
936 : }
937 :
938 78 : for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
939 : {
940 52 : if (bHasDifferentZ)
941 : {
942 2 : WriteValue(0, "VERTEX");
943 2 : WriteCore(poFeature);
944 2 : WriteValue(100, "AcDbVertex");
945 2 : WriteValue(100, "AcDb3dPolylineVertex");
946 : }
947 52 : WriteValue(10, poLS->getX(iVert));
948 52 : if (!WriteValue(20, poLS->getY(iVert)))
949 0 : return OGRERR_FAILURE;
950 :
951 52 : if (bHasDifferentZ)
952 : {
953 2 : if (!WriteValue(30, poLS->getZ(iVert)))
954 0 : return OGRERR_FAILURE;
955 2 : WriteValue(70, 32);
956 : }
957 : }
958 :
959 26 : if (bHasDifferentZ)
960 : {
961 1 : WriteValue(0, "SEQEND");
962 1 : WriteCore(poFeature);
963 : }
964 :
965 26 : delete poTool;
966 :
967 26 : return OGRERR_NONE;
968 :
969 : #ifdef notdef
970 : /* -------------------------------------------------------------------- */
971 : /* Alternate unmaintained implementation as a polyline entity. */
972 : /* -------------------------------------------------------------------- */
973 : WriteValue(0, "POLYLINE");
974 : WriteCore(poFeature);
975 : WriteValue(100, "AcDbPolyline");
976 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
977 : WriteValue(70, 1);
978 : else
979 : WriteValue(70, 0);
980 : WriteValue(66, "1");
981 :
982 : for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
983 : {
984 : WriteValue(0, "VERTEX");
985 : WriteValue(8, "0");
986 : WriteValue(10, poLS->getX(iVert));
987 : if (!WriteValue(20, poLS->getY(iVert)))
988 : return OGRERR_FAILURE;
989 :
990 : if (poLS->getGeometryType() == wkbLineString25D)
991 : {
992 : if (!WriteValue(30, poLS->getZ(iVert)))
993 : return OGRERR_FAILURE;
994 : }
995 : }
996 :
997 : WriteValue(0, "SEQEND");
998 : WriteValue(8, "0");
999 :
1000 : return OGRERR_NONE;
1001 : #endif
1002 : }
1003 :
1004 : /************************************************************************/
1005 : /* WriteHATCH() */
1006 : /************************************************************************/
1007 :
1008 23 : OGRErr OGRDXFWriterLayer::WriteHATCH(OGRFeature *poFeature, OGRGeometry *poGeom)
1009 :
1010 : {
1011 : /* -------------------------------------------------------------------- */
1012 : /* For now we handle multipolygons by writing a series of */
1013 : /* entities. */
1014 : /* -------------------------------------------------------------------- */
1015 23 : if (poGeom == nullptr)
1016 19 : poGeom = poFeature->GetGeometryRef();
1017 :
1018 23 : if (poGeom->IsEmpty())
1019 : {
1020 0 : return OGRERR_NONE;
1021 : }
1022 :
1023 23 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
1024 : {
1025 4 : OGRErr eErr = OGRERR_NONE;
1026 8 : for (auto &&poMember : poGeom->toMultiPolygon())
1027 : {
1028 4 : eErr = WriteHATCH(poFeature, poMember);
1029 4 : if (eErr != OGRERR_NONE)
1030 0 : break;
1031 : }
1032 :
1033 4 : return eErr;
1034 : }
1035 :
1036 : /* -------------------------------------------------------------------- */
1037 : /* Do we now have a geometry we can work with? */
1038 : /* -------------------------------------------------------------------- */
1039 20 : if (wkbFlatten(poGeom->getGeometryType()) != wkbPolygon &&
1040 1 : wkbFlatten(poGeom->getGeometryType()) != wkbTriangle)
1041 : {
1042 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
1043 : }
1044 :
1045 : /* -------------------------------------------------------------------- */
1046 : /* Write as a hatch. */
1047 : /* -------------------------------------------------------------------- */
1048 19 : WriteValue(0, "HATCH");
1049 19 : WriteCore(poFeature);
1050 19 : WriteValue(100, "AcDbHatch");
1051 :
1052 : // Figure out "average" elevation
1053 19 : OGREnvelope3D oEnv;
1054 19 : poGeom->getEnvelope(&oEnv);
1055 19 : WriteValue(10, 0); // elevation point X = 0
1056 19 : WriteValue(20, 0); // elevation point Y = 0
1057 : // elevation point Z = constant elevation
1058 19 : WriteValue(30, oEnv.MinZ + (oEnv.MaxZ - oEnv.MinZ) / 2);
1059 :
1060 19 : WriteValue(210, 0); // extrusion direction X
1061 19 : WriteValue(220, 0); // extrusion direction Y
1062 19 : WriteValue(230, 1.0); // extrusion direction Z
1063 :
1064 19 : WriteValue(2, "SOLID"); // fill pattern
1065 19 : WriteValue(70, 1); // solid fill
1066 19 : WriteValue(71, 0); // associativity
1067 :
1068 : /* -------------------------------------------------------------------- */
1069 : /* Do we have styling information? */
1070 : /* -------------------------------------------------------------------- */
1071 19 : OGRStyleTool *poTool = nullptr;
1072 19 : OGRStyleMgr oSM;
1073 :
1074 19 : if (poFeature->GetStyleString() != nullptr)
1075 : {
1076 1 : oSM.InitFromFeature(poFeature);
1077 :
1078 1 : if (oSM.GetPartCount() > 0)
1079 1 : poTool = oSM.GetPart(0);
1080 : }
1081 : // Write style brush fore color
1082 19 : if (poTool && poTool->GetType() == OGRSTCBrush)
1083 : {
1084 1 : OGRStyleBrush *poBrush = (OGRStyleBrush *)poTool;
1085 : GBool bDefault;
1086 :
1087 1 : if (poBrush->ForeColor(bDefault) != nullptr && !bDefault)
1088 1 : WriteValue(62, ColorStringToDXFColor(poBrush->ForeColor(bDefault)));
1089 : }
1090 19 : delete poTool;
1091 :
1092 : /* -------------------------------------------------------------------- */
1093 : /* Handle a PEN tool to control drawing color and width. */
1094 : /* Perhaps one day also dottedness, etc. */
1095 : /* -------------------------------------------------------------------- */
1096 : #ifdef notdef
1097 : if (poTool && poTool->GetType() == OGRSTCPen)
1098 : {
1099 : OGRStylePen *poPen = (OGRStylePen *)poTool;
1100 : GBool bDefault;
1101 :
1102 : if (poPen->Color(bDefault) != NULL && !bDefault)
1103 : WriteValue(62, ColorStringToDXFColor(poPen->Color(bDefault)));
1104 :
1105 : double dfWidthInMM = poPen->Width(bDefault);
1106 :
1107 : if (!bDefault)
1108 : WriteValue(370, (int)floor(dfWidthInMM * 100 + 0.5));
1109 : }
1110 :
1111 : /* -------------------------------------------------------------------- */
1112 : /* Do we have a Linetype for the feature? */
1113 : /* -------------------------------------------------------------------- */
1114 : CPLString osLineType = poFeature->GetFieldAsString("Linetype");
1115 :
1116 : if (!osLineType.empty() &&
1117 : (poDS->oHeaderDS.LookupLineType(osLineType) != NULL ||
1118 : oNewLineTypes.count(osLineType) > 0))
1119 : {
1120 : // Already define -> just reference it.
1121 : WriteValue(6, osLineType);
1122 : }
1123 : else if (poTool != NULL && poTool->GetType() == OGRSTCPen)
1124 : {
1125 : CPLString osDefinition = PrepareLineTypeDefinition(poFeature, poTool);
1126 :
1127 : if (osDefinition != "" && osLineType == "")
1128 : {
1129 : // Is this definition already created and named?
1130 : std::map<CPLString, CPLString>::iterator it;
1131 :
1132 : for (it = oNewLineTypes.begin(); it != oNewLineTypes.end(); it++)
1133 : {
1134 : if ((*it).second == osDefinition)
1135 : {
1136 : osLineType = (*it).first;
1137 : break;
1138 : }
1139 : }
1140 :
1141 : // create an automatic name for it.
1142 : if (osLineType == "")
1143 : {
1144 : do
1145 : {
1146 : osLineType.Printf("AutoLineType-%d", nNextAutoID++);
1147 : } while (poDS->oHeaderDS.LookupLineType(osLineType) != NULL);
1148 : }
1149 : }
1150 :
1151 : // If it isn't already defined, add it now.
1152 : if (osDefinition != "" && oNewLineTypes.count(osLineType) == 0)
1153 : {
1154 : oNewLineTypes[osLineType] = osDefinition;
1155 : WriteValue(6, osLineType);
1156 : }
1157 : }
1158 : delete poTool;
1159 : #endif
1160 :
1161 : /* -------------------------------------------------------------------- */
1162 : /* Process the loops (rings). */
1163 : /* -------------------------------------------------------------------- */
1164 19 : OGRPolygon *poPoly = poGeom->toPolygon();
1165 :
1166 19 : WriteValue(91, poPoly->getNumInteriorRings() + 1);
1167 :
1168 39 : for (auto &&poLR : *poPoly)
1169 : {
1170 20 : WriteValue(92, 2); // Polyline
1171 20 : WriteValue(72, 0); // has bulge
1172 20 : WriteValue(73, 1); // is closed
1173 20 : WriteValue(93, poLR->getNumPoints());
1174 :
1175 116 : for (int iVert = 0; iVert < poLR->getNumPoints(); iVert++)
1176 : {
1177 96 : WriteValue(10, poLR->getX(iVert));
1178 96 : WriteValue(20, poLR->getY(iVert));
1179 : }
1180 :
1181 20 : WriteValue(97, 0); // 0 source boundary objects
1182 : }
1183 :
1184 19 : WriteValue(75, 0); // hatch style = Hatch "odd parity" area (Normal style)
1185 19 : WriteValue(76, 1); // hatch pattern type = predefined
1186 19 : WriteValue(98, 0); // 0 seed points
1187 :
1188 19 : return OGRERR_NONE;
1189 :
1190 : #ifdef notdef
1191 : /* -------------------------------------------------------------------- */
1192 : /* Alternate unmaintained implementation as a polyline entity. */
1193 : /* -------------------------------------------------------------------- */
1194 : WriteValue(0, "POLYLINE");
1195 : WriteCore(poFeature);
1196 : WriteValue(100, "AcDbPolyline");
1197 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
1198 : WriteValue(70, 1);
1199 : else
1200 : WriteValue(70, 0);
1201 : WriteValue(66, "1");
1202 :
1203 : for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
1204 : {
1205 : WriteValue(0, "VERTEX");
1206 : WriteValue(8, "0");
1207 : WriteValue(10, poLS->getX(iVert));
1208 : if (!WriteValue(20, poLS->getY(iVert)))
1209 : return OGRERR_FAILURE;
1210 :
1211 : if (poLS->getGeometryType() == wkbLineString25D)
1212 : {
1213 : if (!WriteValue(30, poLS->getZ(iVert)))
1214 : return OGRERR_FAILURE;
1215 : }
1216 : }
1217 :
1218 : WriteValue(0, "SEQEND");
1219 : WriteValue(8, "0");
1220 :
1221 : return OGRERR_NONE;
1222 : #endif
1223 : }
1224 :
1225 : /************************************************************************/
1226 : /* ICreateFeature() */
1227 : /************************************************************************/
1228 :
1229 210 : OGRErr OGRDXFWriterLayer::ICreateFeature(OGRFeature *poFeature)
1230 :
1231 : {
1232 210 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1233 210 : OGRwkbGeometryType eGType = wkbNone;
1234 :
1235 210 : if (poGeom != nullptr)
1236 : {
1237 177 : if (!poGeom->IsEmpty())
1238 : {
1239 175 : OGREnvelope sEnvelope;
1240 175 : poGeom->getEnvelope(&sEnvelope);
1241 175 : poDS->UpdateExtent(&sEnvelope);
1242 : }
1243 177 : eGType = wkbFlatten(poGeom->getGeometryType());
1244 : }
1245 :
1246 210 : if (eGType == wkbPoint)
1247 : {
1248 119 : const char *pszBlockName = poFeature->GetFieldAsString("BlockName");
1249 :
1250 : // We don't want to treat as a blocks ref if the block is not defined
1251 238 : if (pszBlockName &&
1252 119 : poDS->oHeaderDS.LookupBlock(pszBlockName) == nullptr)
1253 : {
1254 124 : if (poDS->poBlocksLayer == nullptr ||
1255 7 : poDS->poBlocksLayer->FindBlock(pszBlockName) == nullptr)
1256 112 : pszBlockName = nullptr;
1257 : }
1258 :
1259 119 : if (pszBlockName != nullptr)
1260 7 : return WriteINSERT(poFeature);
1261 :
1262 113 : else if (poFeature->GetStyleString() != nullptr &&
1263 1 : STARTS_WITH_CI(poFeature->GetStyleString(), "LABEL"))
1264 1 : return WriteTEXT(poFeature);
1265 : else
1266 111 : return WritePOINT(poFeature);
1267 : }
1268 91 : else if (eGType == wkbLineString || eGType == wkbMultiLineString)
1269 26 : return WritePOLYLINE(poFeature);
1270 :
1271 65 : else if (eGType == wkbPolygon || eGType == wkbTriangle ||
1272 : eGType == wkbMultiPolygon)
1273 : {
1274 19 : if (bWriteHatch)
1275 19 : return WriteHATCH(poFeature);
1276 : else
1277 0 : return WritePOLYLINE(poFeature);
1278 : }
1279 :
1280 : // Explode geometry collections into multiple entities.
1281 46 : else if (eGType == wkbGeometryCollection)
1282 : {
1283 : OGRGeometryCollection *poGC =
1284 8 : poFeature->StealGeometry()->toGeometryCollection();
1285 26 : for (auto &&poMember : poGC)
1286 : {
1287 19 : poFeature->SetGeometry(poMember);
1288 :
1289 19 : OGRErr eErr = CreateFeature(poFeature);
1290 :
1291 19 : if (eErr != OGRERR_NONE)
1292 : {
1293 1 : delete poGC;
1294 1 : return eErr;
1295 : }
1296 : }
1297 :
1298 7 : poFeature->SetGeometryDirectly(poGC);
1299 7 : return OGRERR_NONE;
1300 : }
1301 : else
1302 : {
1303 38 : CPLError(CE_Failure, CPLE_AppDefined,
1304 : "No known way to write feature with geometry '%s'.",
1305 : OGRGeometryTypeToName(eGType));
1306 38 : return OGRERR_FAILURE;
1307 : }
1308 : }
1309 :
1310 : /************************************************************************/
1311 : /* ColorStringToDXFColor() */
1312 : /************************************************************************/
1313 :
1314 7 : int OGRDXFWriterLayer::ColorStringToDXFColor(const char *pszRGB)
1315 :
1316 : {
1317 : /* -------------------------------------------------------------------- */
1318 : /* Parse the RGB string. */
1319 : /* -------------------------------------------------------------------- */
1320 7 : if (pszRGB == nullptr)
1321 0 : return -1;
1322 :
1323 7 : unsigned int nRed = 0;
1324 7 : unsigned int nGreen = 0;
1325 7 : unsigned int nBlue = 0;
1326 7 : unsigned int nTransparency = 255;
1327 :
1328 : const int nCount =
1329 7 : sscanf(pszRGB, "#%2x%2x%2x%2x", &nRed, &nGreen, &nBlue, &nTransparency);
1330 :
1331 7 : if (nCount < 3)
1332 0 : return -1;
1333 :
1334 : /* -------------------------------------------------------------------- */
1335 : /* Find near color in DXF palette. */
1336 : /* -------------------------------------------------------------------- */
1337 7 : const unsigned char *pabyDXFColors = ACGetColorTable();
1338 7 : int nMinDist = 768;
1339 7 : int nBestColor = -1;
1340 :
1341 1792 : for (int i = 1; i < 256; i++)
1342 : {
1343 1785 : const int nDist =
1344 1785 : std::abs(static_cast<int>(nRed) - pabyDXFColors[i * 3 + 0]) +
1345 1785 : std::abs(static_cast<int>(nGreen) - pabyDXFColors[i * 3 + 1]) +
1346 1785 : std::abs(static_cast<int>(nBlue) - pabyDXFColors[i * 3 + 2]);
1347 :
1348 1785 : if (nDist < nMinDist)
1349 : {
1350 12 : nBestColor = i;
1351 12 : nMinDist = nDist;
1352 : }
1353 : }
1354 :
1355 7 : return nBestColor;
1356 : }
1357 :
1358 : /************************************************************************/
1359 : /* GetDataset() */
1360 : /************************************************************************/
1361 :
1362 17 : GDALDataset *OGRDXFWriterLayer::GetDataset()
1363 : {
1364 17 : return poDS;
1365 : }
|