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