1 : /******************************************************************************
2 : * $Id: ogrdxfwriterlayer.cpp 24991 2012-09-29 10:13:28Z rouault $
3 : *
4 : * Project: DXF Translator
5 : * Purpose: Implements OGRDXFWriterLayer - the OGRLayer class used for
6 : * writing a DXF file.
7 : * Author: Frank Warmerdam, warmerdam@pobox.com
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.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 : CPL_CVSID("$Id: ogrdxfwriterlayer.cpp 24991 2012-09-29 10:13:28Z rouault $");
37 :
38 : #ifndef PI
39 : #define PI 3.14159265358979323846
40 : #endif
41 :
42 : /************************************************************************/
43 : /* OGRDXFWriterLayer() */
44 : /************************************************************************/
45 :
46 7 : OGRDXFWriterLayer::OGRDXFWriterLayer( OGRDXFWriterDS *poDS, VSILFILE *fp )
47 :
48 : {
49 7 : this->fp = fp;
50 7 : this->poDS = poDS;
51 :
52 7 : nNextAutoID = 1;
53 7 : bWriteHatch = CSLTestBoolean(CPLGetConfigOption("DXF_WRITE_HATCH", "YES"));
54 :
55 7 : poFeatureDefn = new OGRFeatureDefn( "entities" );
56 7 : poFeatureDefn->Reference();
57 :
58 7 : OGRFieldDefn oLayerField( "Layer", OFTString );
59 7 : poFeatureDefn->AddFieldDefn( &oLayerField );
60 :
61 7 : OGRFieldDefn oClassField( "SubClasses", OFTString );
62 7 : poFeatureDefn->AddFieldDefn( &oClassField );
63 :
64 7 : OGRFieldDefn oExtendedField( "ExtendedEntity", OFTString );
65 7 : poFeatureDefn->AddFieldDefn( &oExtendedField );
66 :
67 7 : OGRFieldDefn oLinetypeField( "Linetype", OFTString );
68 7 : poFeatureDefn->AddFieldDefn( &oLinetypeField );
69 :
70 7 : OGRFieldDefn oEntityHandleField( "EntityHandle", OFTString );
71 7 : poFeatureDefn->AddFieldDefn( &oEntityHandleField );
72 :
73 7 : OGRFieldDefn oTextField( "Text", OFTString );
74 7 : poFeatureDefn->AddFieldDefn( &oTextField );
75 :
76 7 : OGRFieldDefn oBlockField( "BlockName", OFTString );
77 7 : poFeatureDefn->AddFieldDefn( &oBlockField );
78 :
79 7 : OGRFieldDefn oScaleField( "BlockScale", OFTRealList );
80 7 : poFeatureDefn->AddFieldDefn( &oScaleField );
81 :
82 7 : OGRFieldDefn oBlockAngleField( "BlockAngle", OFTReal );
83 7 : poFeatureDefn->AddFieldDefn( &oBlockAngleField );
84 7 : }
85 :
86 : /************************************************************************/
87 : /* ~OGRDXFWriterLayer() */
88 : /************************************************************************/
89 :
90 7 : OGRDXFWriterLayer::~OGRDXFWriterLayer()
91 :
92 : {
93 7 : if( poFeatureDefn )
94 7 : poFeatureDefn->Release();
95 7 : }
96 :
97 : /************************************************************************/
98 : /* ResetFP() */
99 : /* */
100 : /* Redirect output. Mostly used for writing block definitions. */
101 : /************************************************************************/
102 :
103 1 : void OGRDXFWriterLayer::ResetFP( VSILFILE *fpNew )
104 :
105 : {
106 1 : fp = fpNew;
107 1 : }
108 :
109 : /************************************************************************/
110 : /* TestCapability() */
111 : /************************************************************************/
112 :
113 0 : int OGRDXFWriterLayer::TestCapability( const char * pszCap )
114 :
115 : {
116 0 : if( EQUAL(pszCap,OLCStringsAsUTF8) )
117 0 : return TRUE;
118 0 : else if( EQUAL(pszCap,OLCSequentialWrite) )
119 0 : return TRUE;
120 : else
121 0 : return FALSE;
122 : }
123 :
124 : /************************************************************************/
125 : /* CreateField() */
126 : /* */
127 : /* This is really a dummy as our fields are precreated. */
128 : /************************************************************************/
129 :
130 0 : OGRErr OGRDXFWriterLayer::CreateField( OGRFieldDefn *poField,
131 : int bApproxOK )
132 :
133 : {
134 0 : if( poFeatureDefn->GetFieldIndex(poField->GetNameRef()) >= 0
135 : && bApproxOK )
136 0 : return OGRERR_NONE;
137 :
138 : CPLError( CE_Failure, CPLE_AppDefined,
139 : "DXF layer does not support arbitrary field creation, field '%s' not created.",
140 0 : poField->GetNameRef() );
141 :
142 0 : return OGRERR_FAILURE;
143 : }
144 :
145 : /************************************************************************/
146 : /* WriteValue() */
147 : /************************************************************************/
148 :
149 75 : int OGRDXFWriterLayer::WriteValue( int nCode, const char *pszValue )
150 :
151 : {
152 75 : CPLString osLinePair;
153 :
154 75 : osLinePair.Printf( "%3d\n", nCode );
155 :
156 75 : if( strlen(pszValue) < 255 )
157 75 : osLinePair += pszValue;
158 : else
159 0 : osLinePair.append( pszValue, 255 );
160 :
161 75 : osLinePair += "\n";
162 :
163 : return VSIFWriteL( osLinePair.c_str(),
164 75 : 1, osLinePair.size(), fp ) == osLinePair.size();
165 : }
166 :
167 : /************************************************************************/
168 : /* WriteValue() */
169 : /************************************************************************/
170 :
171 75 : int OGRDXFWriterLayer::WriteValue( int nCode, int nValue )
172 :
173 : {
174 75 : CPLString osLinePair;
175 :
176 75 : osLinePair.Printf( "%3d\n%d\n", nCode, nValue );
177 :
178 : return VSIFWriteL( osLinePair.c_str(),
179 75 : 1, osLinePair.size(), fp ) == osLinePair.size();
180 : }
181 :
182 : /************************************************************************/
183 : /* WriteValue() */
184 : /************************************************************************/
185 :
186 86 : int OGRDXFWriterLayer::WriteValue( int nCode, double dfValue )
187 :
188 : {
189 : char szLinePair[64];
190 :
191 86 : snprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue );
192 86 : char* pszComma = strchr(szLinePair, ',');
193 86 : if (pszComma)
194 0 : *pszComma = '.';
195 86 : size_t nLen = strlen(szLinePair);
196 :
197 : return VSIFWriteL( szLinePair,
198 86 : 1, nLen, fp ) == nLen;
199 : }
200 :
201 : /************************************************************************/
202 : /* WriteCore() */
203 : /* */
204 : /* Write core fields common to all sorts of elements. */
205 : /************************************************************************/
206 :
207 16 : OGRErr OGRDXFWriterLayer::WriteCore( OGRFeature *poFeature )
208 :
209 : {
210 : /* -------------------------------------------------------------------- */
211 : /* Write out an entity id. I'm not sure why this is critical, */
212 : /* but it seems that VoloView will just quietly fail to open */
213 : /* dxf files without entity ids set on most/all entities. */
214 : /* Also, for reasons I don't understand these ids seem to have */
215 : /* to start somewhere around 0x50 hex (80 decimal). */
216 : /* -------------------------------------------------------------------- */
217 16 : poFeature->SetFID( poDS->WriteEntityID(fp,poFeature->GetFID()) );
218 :
219 : /* -------------------------------------------------------------------- */
220 : /* For now we assign everything to the default layer - layer */
221 : /* "0" - if there is no layer property on the source features. */
222 : /* -------------------------------------------------------------------- */
223 16 : const char *pszLayer = poFeature->GetFieldAsString( "Layer" );
224 27 : if( pszLayer == NULL || strlen(pszLayer) == 0 )
225 : {
226 11 : WriteValue( 8, "0" );
227 : }
228 : else
229 : {
230 : const char *pszExists =
231 5 : poDS->oHeaderDS.LookupLayerProperty( pszLayer, "Exists" );
232 5 : if( (pszExists == NULL || strlen(pszExists) == 0)
233 : && CSLFindString( poDS->papszLayersToCreate, pszLayer ) == -1 )
234 : {
235 : poDS->papszLayersToCreate =
236 3 : CSLAddString( poDS->papszLayersToCreate, pszLayer );
237 : }
238 :
239 5 : WriteValue( 8, pszLayer );
240 : }
241 :
242 16 : return OGRERR_NONE;
243 : }
244 :
245 : /************************************************************************/
246 : /* WriteINSERT() */
247 : /************************************************************************/
248 :
249 4 : OGRErr OGRDXFWriterLayer::WriteINSERT( OGRFeature *poFeature )
250 :
251 : {
252 4 : WriteValue( 0, "INSERT" );
253 4 : WriteCore( poFeature );
254 4 : WriteValue( 100, "AcDbEntity" );
255 4 : WriteValue( 100, "AcDbBlockReference" );
256 4 : WriteValue( 2, poFeature->GetFieldAsString("BlockName") );
257 :
258 : // Write style symbol color
259 4 : OGRStyleTool *poTool = NULL;
260 4 : OGRStyleMgr oSM;
261 4 : if( poFeature->GetStyleString() != NULL )
262 : {
263 0 : oSM.InitFromFeature( poFeature );
264 :
265 0 : if( oSM.GetPartCount() > 0 )
266 0 : poTool = oSM.GetPart(0);
267 : }
268 4 : if( poTool && poTool->GetType() == OGRSTCSymbol )
269 : {
270 0 : OGRStyleSymbol *poSymbol = (OGRStyleSymbol *) poTool;
271 : GBool bDefault;
272 :
273 0 : if( poSymbol->Color(bDefault) != NULL && !bDefault )
274 0 : WriteValue( 62, ColorStringToDXFColor( poSymbol->Color(bDefault) ) );
275 : }
276 4 : delete poTool;
277 :
278 : /* -------------------------------------------------------------------- */
279 : /* Write location. */
280 : /* -------------------------------------------------------------------- */
281 4 : OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef();
282 :
283 4 : WriteValue( 10, poPoint->getX() );
284 4 : if( !WriteValue( 20, poPoint->getY() ) )
285 0 : return OGRERR_FAILURE;
286 :
287 4 : if( poPoint->getGeometryType() == wkbPoint25D )
288 : {
289 0 : if( !WriteValue( 30, poPoint->getZ() ) )
290 0 : return OGRERR_FAILURE;
291 : }
292 :
293 : /* -------------------------------------------------------------------- */
294 : /* Write scaling. */
295 : /* -------------------------------------------------------------------- */
296 : int nScaleCount;
297 : const double *padfScale =
298 4 : poFeature->GetFieldAsDoubleList( "BlockScale", &nScaleCount );
299 :
300 4 : if( nScaleCount == 3 )
301 : {
302 1 : WriteValue( 41, padfScale[0] );
303 1 : WriteValue( 42, padfScale[1] );
304 1 : WriteValue( 43, padfScale[2] );
305 : }
306 :
307 : /* -------------------------------------------------------------------- */
308 : /* Write rotation. */
309 : /* -------------------------------------------------------------------- */
310 4 : double dfAngle = poFeature->GetFieldAsDouble( "BlockAngle" );
311 :
312 4 : if( dfAngle != 0.0 )
313 : {
314 1 : WriteValue( 50, dfAngle ); // degrees
315 : }
316 :
317 4 : return OGRERR_NONE;
318 : }
319 :
320 : /************************************************************************/
321 : /* WritePOINT() */
322 : /************************************************************************/
323 :
324 1 : OGRErr OGRDXFWriterLayer::WritePOINT( OGRFeature *poFeature )
325 :
326 : {
327 1 : WriteValue( 0, "POINT" );
328 1 : WriteCore( poFeature );
329 1 : WriteValue( 100, "AcDbEntity" );
330 1 : WriteValue( 100, "AcDbPoint" );
331 :
332 : // Write style pen color
333 1 : OGRStyleTool *poTool = NULL;
334 1 : OGRStyleMgr oSM;
335 1 : if( poFeature->GetStyleString() != NULL )
336 : {
337 0 : oSM.InitFromFeature( poFeature );
338 :
339 0 : if( oSM.GetPartCount() > 0 )
340 0 : poTool = oSM.GetPart(0);
341 : }
342 1 : if( poTool && poTool->GetType() == OGRSTCPen )
343 : {
344 0 : OGRStylePen *poPen = (OGRStylePen *) poTool;
345 : GBool bDefault;
346 :
347 0 : if( poPen->Color(bDefault) != NULL && !bDefault )
348 0 : WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) );
349 : }
350 1 : delete poTool;
351 :
352 1 : OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef();
353 :
354 1 : WriteValue( 10, poPoint->getX() );
355 1 : if( !WriteValue( 20, poPoint->getY() ) )
356 0 : return OGRERR_FAILURE;
357 :
358 1 : if( poPoint->getGeometryType() == wkbPoint25D )
359 : {
360 0 : if( !WriteValue( 30, poPoint->getZ() ) )
361 0 : return OGRERR_FAILURE;
362 : }
363 :
364 1 : return OGRERR_NONE;
365 : }
366 :
367 : /************************************************************************/
368 : /* TextEscape() */
369 : /* */
370 : /* Translate UTF8 to Win1252 and escape special characters like */
371 : /* newline and space with DXF style escapes. Note that */
372 : /* non-win1252 unicode characters are translated using the */
373 : /* unicode escape sequence. */
374 : /************************************************************************/
375 :
376 1 : CPLString OGRDXFWriterLayer::TextEscape( const char *pszInput )
377 :
378 : {
379 1 : CPLString osResult;
380 : wchar_t *panInput = CPLRecodeToWChar( pszInput,
381 : CPL_ENC_UTF8,
382 1 : CPL_ENC_UCS2 );
383 : int i;
384 :
385 :
386 10 : for( i = 0; panInput[i] != 0; i++ )
387 : {
388 9 : if( panInput[i] == '\n' )
389 0 : osResult += "\\P";
390 9 : else if( panInput[i] == ' ' )
391 0 : osResult += "\\~";
392 9 : else if( panInput[i] == '\\' )
393 0 : osResult += "\\\\";
394 9 : else if( panInput[i] > 255 )
395 : {
396 0 : CPLString osUnicode;
397 0 : osUnicode.Printf( "\\U+%04x", (int) panInput[i] );
398 0 : osResult += osUnicode;
399 : }
400 : else
401 9 : osResult += (char) panInput[i];
402 : }
403 :
404 1 : CPLFree(panInput);
405 :
406 0 : return osResult;
407 : }
408 :
409 : /************************************************************************/
410 : /* WriteTEXT() */
411 : /************************************************************************/
412 :
413 1 : OGRErr OGRDXFWriterLayer::WriteTEXT( OGRFeature *poFeature )
414 :
415 : {
416 1 : WriteValue( 0, "MTEXT" );
417 1 : WriteCore( poFeature );
418 1 : WriteValue( 100, "AcDbEntity" );
419 1 : WriteValue( 100, "AcDbMText" );
420 :
421 : /* -------------------------------------------------------------------- */
422 : /* Do we have styling information? */
423 : /* -------------------------------------------------------------------- */
424 1 : OGRStyleTool *poTool = NULL;
425 1 : OGRStyleMgr oSM;
426 :
427 1 : if( poFeature->GetStyleString() != NULL )
428 : {
429 1 : oSM.InitFromFeature( poFeature );
430 :
431 1 : if( oSM.GetPartCount() > 0 )
432 1 : poTool = oSM.GetPart(0);
433 : }
434 :
435 : /* ==================================================================== */
436 : /* Process the LABEL tool. */
437 : /* ==================================================================== */
438 1 : if( poTool && poTool->GetType() == OGRSTCLabel )
439 : {
440 1 : OGRStyleLabel *poLabel = (OGRStyleLabel *) poTool;
441 : GBool bDefault;
442 :
443 : /* -------------------------------------------------------------------- */
444 : /* Color */
445 : /* -------------------------------------------------------------------- */
446 1 : if( poLabel->ForeColor(bDefault) != NULL && !bDefault )
447 : WriteValue( 62, ColorStringToDXFColor(
448 1 : poLabel->ForeColor(bDefault) ) );
449 :
450 : /* -------------------------------------------------------------------- */
451 : /* Angle */
452 : /* -------------------------------------------------------------------- */
453 1 : double dfAngle = poLabel->Angle(bDefault);
454 :
455 : // The DXF2000 reference says this is in radians, but in files
456 : // I see it seems to be in degrees. Perhaps this is version dependent?
457 1 : if( !bDefault )
458 1 : WriteValue( 50, dfAngle );
459 :
460 : /* -------------------------------------------------------------------- */
461 : /* Height - We need to fetch this in georeferenced units - I'm */
462 : /* doubt the default translation mechanism will be much good. */
463 : /* -------------------------------------------------------------------- */
464 1 : poTool->SetUnit( OGRSTUGround );
465 1 : double dfHeight = poLabel->Size(bDefault);
466 :
467 1 : if( !bDefault )
468 1 : WriteValue( 40, dfHeight );
469 :
470 : /* -------------------------------------------------------------------- */
471 : /* Anchor / Attachment Point */
472 : /* -------------------------------------------------------------------- */
473 1 : int nAnchor = poLabel->Anchor(bDefault);
474 :
475 1 : if( !bDefault )
476 : {
477 : const static int anAnchorMap[] =
478 : { -1, 7, 8, 9, 4, 5, 6, 1, 2, 3, 7, 8, 9 };
479 :
480 0 : if( nAnchor > 0 && nAnchor < 13 )
481 0 : WriteValue( 71, anAnchorMap[nAnchor] );
482 : }
483 :
484 : /* -------------------------------------------------------------------- */
485 : /* Escape the text, and convert to ISO8859. */
486 : /* -------------------------------------------------------------------- */
487 1 : const char *pszText = poLabel->TextString( bDefault );
488 :
489 1 : if( pszText != NULL && !bDefault )
490 : {
491 1 : CPLString osEscaped = TextEscape( pszText );
492 1 : WriteValue( 1, osEscaped );
493 : }
494 : }
495 :
496 1 : delete poTool;
497 :
498 : /* -------------------------------------------------------------------- */
499 : /* Write the location. */
500 : /* -------------------------------------------------------------------- */
501 1 : OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef();
502 :
503 1 : WriteValue( 10, poPoint->getX() );
504 1 : if( !WriteValue( 20, poPoint->getY() ) )
505 0 : return OGRERR_FAILURE;
506 :
507 1 : if( poPoint->getGeometryType() == wkbPoint25D )
508 : {
509 1 : if( !WriteValue( 30, poPoint->getZ() ) )
510 0 : return OGRERR_FAILURE;
511 : }
512 :
513 1 : return OGRERR_NONE;
514 :
515 : }
516 :
517 : /************************************************************************/
518 : /* PrepareLineTypeDefinition() */
519 : /************************************************************************/
520 : CPLString
521 2 : OGRDXFWriterLayer::PrepareLineTypeDefinition( OGRFeature *poFeature,
522 : OGRStyleTool *poTool )
523 :
524 : {
525 2 : CPLString osDef;
526 2 : OGRStylePen *poPen = (OGRStylePen *) poTool;
527 : GBool bDefault;
528 : const char *pszPattern;
529 :
530 : /* -------------------------------------------------------------------- */
531 : /* Fetch pattern. */
532 : /* -------------------------------------------------------------------- */
533 2 : pszPattern = poPen->Pattern( bDefault );
534 2 : if( bDefault || strlen(pszPattern) == 0 )
535 0 : return "";
536 :
537 : /* -------------------------------------------------------------------- */
538 : /* Split into pen up / pen down bits. */
539 : /* -------------------------------------------------------------------- */
540 2 : char **papszTokens = CSLTokenizeString(pszPattern);
541 : int i;
542 2 : double dfTotalLength = 0;
543 :
544 6 : for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++ )
545 : {
546 4 : const char *pszToken = papszTokens[i];
547 : const char *pszUnit;
548 4 : CPLString osAmount;
549 4 : CPLString osDXFEntry;
550 :
551 : // Split amount and unit.
552 4 : for( pszUnit = pszToken;
553 : strchr( "0123456789.", *pszUnit) != NULL;
554 : pszUnit++ ) {}
555 :
556 4 : osAmount.assign(pszToken,(int) (pszUnit-pszToken));
557 :
558 : // If the unit is other than 'g' we really should be trying to
559 : // do some type of transformation - but what to do? Pretty hard.
560 :
561 : //
562 :
563 : // Even entries are "pen down" represented as negative in DXF.
564 4 : if( i%2 == 0 )
565 2 : osDXFEntry.Printf( " 49\n-%s\n 74\n0\n", osAmount.c_str() );
566 : else
567 2 : osDXFEntry.Printf( " 49\n%s\n 74\n0\n", osAmount.c_str() );
568 :
569 4 : osDef += osDXFEntry;
570 :
571 4 : dfTotalLength += atof(osAmount);
572 : }
573 :
574 : /* -------------------------------------------------------------------- */
575 : /* Prefix 73 and 40 items to the definition. */
576 : /* -------------------------------------------------------------------- */
577 2 : CPLString osPrefix;
578 :
579 : osPrefix.Printf( " 73\n%d\n 40\n%.6g\n",
580 : CSLCount(papszTokens),
581 2 : dfTotalLength );
582 2 : osDef = osPrefix + osDef;
583 :
584 2 : CSLDestroy( papszTokens );
585 :
586 2 : return osDef;
587 : }
588 :
589 : /************************************************************************/
590 : /* WritePOLYLINE() */
591 : /************************************************************************/
592 :
593 7 : OGRErr OGRDXFWriterLayer::WritePOLYLINE( OGRFeature *poFeature,
594 : OGRGeometry *poGeom )
595 :
596 : {
597 : /* -------------------------------------------------------------------- */
598 : /* For now we handle multilinestrings by writing a series of */
599 : /* entities. */
600 : /* -------------------------------------------------------------------- */
601 7 : if( poGeom == NULL )
602 7 : poGeom = poFeature->GetGeometryRef();
603 :
604 7 : if ( poGeom->IsEmpty() )
605 : {
606 0 : return OGRERR_NONE;
607 : }
608 :
609 14 : if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon
610 7 : || wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString )
611 : {
612 0 : OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom;
613 : int iGeom;
614 0 : OGRErr eErr = OGRERR_NONE;
615 :
616 0 : for( iGeom = 0;
617 : eErr == OGRERR_NONE && iGeom < poGC->getNumGeometries();
618 : iGeom++ )
619 : {
620 0 : eErr = WritePOLYLINE( poFeature, poGC->getGeometryRef( iGeom ) );
621 : }
622 :
623 0 : return eErr;
624 : }
625 :
626 : /* -------------------------------------------------------------------- */
627 : /* Polygons are written with on entity per ring. */
628 : /* -------------------------------------------------------------------- */
629 7 : if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
630 : {
631 0 : OGRPolygon *poPoly = (OGRPolygon *) poGeom;
632 : int iGeom;
633 : OGRErr eErr;
634 :
635 0 : eErr = WritePOLYLINE( poFeature, poPoly->getExteriorRing() );
636 0 : for( iGeom = 0;
637 : eErr == OGRERR_NONE && iGeom < poPoly->getNumInteriorRings();
638 : iGeom++ )
639 : {
640 0 : eErr = WritePOLYLINE( poFeature, poPoly->getInteriorRing(iGeom) );
641 : }
642 :
643 0 : return eErr;
644 : }
645 :
646 : /* -------------------------------------------------------------------- */
647 : /* Do we now have a geometry we can work with? */
648 : /* -------------------------------------------------------------------- */
649 7 : if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
650 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
651 :
652 7 : OGRLineString *poLS = (OGRLineString *) poGeom;
653 :
654 : /* -------------------------------------------------------------------- */
655 : /* Write as a lightweight polygon. */
656 : /* -------------------------------------------------------------------- */
657 7 : WriteValue( 0, "LWPOLYLINE" );
658 7 : WriteCore( poFeature );
659 7 : WriteValue( 100, "AcDbEntity" );
660 7 : WriteValue( 100, "AcDbPolyline" );
661 7 : if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
662 0 : WriteValue( 70, 1 );
663 : else
664 7 : WriteValue( 70, 0 );
665 7 : WriteValue( 90, poLS->getNumPoints() );
666 :
667 : /* -------------------------------------------------------------------- */
668 : /* Do we have styling information? */
669 : /* -------------------------------------------------------------------- */
670 7 : OGRStyleTool *poTool = NULL;
671 7 : OGRStyleMgr oSM;
672 :
673 7 : if( poFeature->GetStyleString() != NULL )
674 : {
675 3 : oSM.InitFromFeature( poFeature );
676 :
677 3 : if( oSM.GetPartCount() > 0 )
678 3 : poTool = oSM.GetPart(0);
679 : }
680 :
681 : /* -------------------------------------------------------------------- */
682 : /* Handle a PEN tool to control drawing color and width. */
683 : /* Perhaps one day also dottedness, etc. */
684 : /* -------------------------------------------------------------------- */
685 7 : if( poTool && poTool->GetType() == OGRSTCPen )
686 : {
687 3 : OGRStylePen *poPen = (OGRStylePen *) poTool;
688 : GBool bDefault;
689 :
690 3 : if( poPen->Color(bDefault) != NULL && !bDefault )
691 3 : WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) );
692 :
693 : // we want to fetch the width in ground units.
694 3 : poPen->SetUnit( OGRSTUGround, 1.0 );
695 3 : double dfWidth = poPen->Width(bDefault);
696 :
697 3 : if( !bDefault )
698 3 : WriteValue( 370, (int) floor(dfWidth * 100 + 0.5) );
699 : }
700 :
701 : /* -------------------------------------------------------------------- */
702 : /* Do we have a Linetype for the feature? */
703 : /* -------------------------------------------------------------------- */
704 7 : CPLString osLineType = poFeature->GetFieldAsString( "Linetype" );
705 :
706 7 : if( osLineType.size() > 0
707 : && (poDS->oHeaderDS.LookupLineType( osLineType ) != NULL
708 : || oNewLineTypes.count(osLineType) > 0 ) )
709 : {
710 : // Already define -> just reference it.
711 1 : WriteValue( 6, osLineType );
712 : }
713 6 : else if( poTool != NULL && poTool->GetType() == OGRSTCPen )
714 : {
715 : CPLString osDefinition = PrepareLineTypeDefinition( poFeature,
716 2 : poTool );
717 :
718 2 : if( osDefinition != "" && osLineType == "" )
719 : {
720 : // Is this definition already created and named?
721 1 : std::map<CPLString,CPLString>::iterator it;
722 :
723 2 : for( it = oNewLineTypes.begin();
724 : it != oNewLineTypes.end();
725 : it++ )
726 : {
727 1 : if( (*it).second == osDefinition )
728 : {
729 0 : osLineType = (*it).first;
730 0 : break;
731 : }
732 : }
733 :
734 : // create an automatic name for it.
735 1 : if( osLineType == "" )
736 : {
737 1 : do
738 : {
739 1 : osLineType.Printf( "AutoLineType-%d", nNextAutoID++ );
740 : }
741 : while( poDS->oHeaderDS.LookupLineType(osLineType) != NULL );
742 : }
743 : }
744 :
745 : // If it isn't already defined, add it now.
746 2 : if( osDefinition != "" && oNewLineTypes.count(osLineType) == 0 )
747 : {
748 2 : oNewLineTypes[osLineType] = osDefinition;
749 2 : WriteValue( 6, osLineType );
750 2 : }
751 : }
752 :
753 : /* -------------------------------------------------------------------- */
754 : /* Write the vertices */
755 : /* -------------------------------------------------------------------- */
756 : int iVert;
757 :
758 21 : for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
759 : {
760 14 : WriteValue( 10, poLS->getX(iVert) );
761 14 : if( !WriteValue( 20, poLS->getY(iVert) ) )
762 0 : return OGRERR_FAILURE;
763 :
764 14 : if( poLS->getGeometryType() == wkbLineString25D )
765 : {
766 0 : if( !WriteValue( 38, poLS->getZ(iVert) ) )
767 0 : return OGRERR_FAILURE;
768 : }
769 : }
770 :
771 7 : delete poTool;
772 :
773 7 : return OGRERR_NONE;
774 :
775 : #ifdef notdef
776 : /* -------------------------------------------------------------------- */
777 : /* Alternate unmaintained implementation as a polyline entity. */
778 : /* -------------------------------------------------------------------- */
779 : WriteValue( 0, "POLYLINE" );
780 : WriteCore( poFeature );
781 : WriteValue( 100, "AcDbEntity" );
782 : WriteValue( 100, "AcDbPolyline" );
783 : if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
784 : WriteValue( 70, 1 );
785 : else
786 : WriteValue( 70, 0 );
787 : WriteValue( 66, "1" );
788 :
789 : int iVert;
790 :
791 : for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
792 : {
793 : WriteValue( 0, "VERTEX" );
794 : WriteValue( 8, "0" );
795 : WriteValue( 10, poLS->getX(iVert) );
796 : if( !WriteValue( 20, poLS->getY(iVert) ) )
797 : return OGRERR_FAILURE;
798 :
799 : if( poLS->getGeometryType() == wkbLineString25D )
800 : {
801 : if( !WriteValue( 30, poLS->getZ(iVert) ) )
802 : return OGRERR_FAILURE;
803 : }
804 : }
805 :
806 : WriteValue( 0, "SEQEND" );
807 : WriteValue( 8, "0" );
808 :
809 : return OGRERR_NONE;
810 : #endif
811 : }
812 :
813 : /************************************************************************/
814 : /* WriteHATCH() */
815 : /************************************************************************/
816 :
817 3 : OGRErr OGRDXFWriterLayer::WriteHATCH( OGRFeature *poFeature,
818 : OGRGeometry *poGeom )
819 :
820 : {
821 : /* -------------------------------------------------------------------- */
822 : /* For now we handle multipolygons by writing a series of */
823 : /* entities. */
824 : /* -------------------------------------------------------------------- */
825 3 : if( poGeom == NULL )
826 3 : poGeom = poFeature->GetGeometryRef();
827 :
828 3 : if ( poGeom->IsEmpty() )
829 : {
830 0 : return OGRERR_NONE;
831 : }
832 :
833 3 : if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon )
834 : {
835 0 : OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom;
836 : int iGeom;
837 0 : OGRErr eErr = OGRERR_NONE;
838 :
839 0 : for( iGeom = 0;
840 : eErr == OGRERR_NONE && iGeom < poGC->getNumGeometries();
841 : iGeom++ )
842 : {
843 0 : eErr = WriteHATCH( poFeature, poGC->getGeometryRef( iGeom ) );
844 : }
845 :
846 0 : return eErr;
847 : }
848 :
849 : /* -------------------------------------------------------------------- */
850 : /* Do we now have a geometry we can work with? */
851 : /* -------------------------------------------------------------------- */
852 3 : if( wkbFlatten(poGeom->getGeometryType()) != wkbPolygon )
853 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
854 :
855 : /* -------------------------------------------------------------------- */
856 : /* Write as a hatch. */
857 : /* -------------------------------------------------------------------- */
858 3 : WriteValue( 0, "HATCH" );
859 3 : WriteCore( poFeature );
860 3 : WriteValue( 100, "AcDbEntity" );
861 3 : WriteValue( 100, "AcDbHatch" );
862 :
863 3 : WriteValue( 10, 0 ); // elevation point X. 0 for DXF
864 3 : WriteValue( 20, 0 ); // elevation point Y
865 3 : WriteValue( 30, 0 ); // elevation point Z
866 3 : WriteValue(210, 0 ); // extrusion direction X
867 3 : WriteValue(220, 0 ); // extrusion direction Y
868 3 : WriteValue(230,1.0); // extrusion direction Z
869 :
870 3 : WriteValue( 2, "SOLID" ); // fill pattern
871 3 : WriteValue( 70, 1 ); // solid fill
872 3 : WriteValue( 71, 0 ); // associativity
873 :
874 : /* -------------------------------------------------------------------- */
875 : /* Do we have styling information? */
876 : /* -------------------------------------------------------------------- */
877 3 : OGRStyleTool *poTool = NULL;
878 3 : OGRStyleMgr oSM;
879 :
880 3 : if( poFeature->GetStyleString() != NULL )
881 : {
882 1 : oSM.InitFromFeature( poFeature );
883 :
884 1 : if( oSM.GetPartCount() > 0 )
885 1 : poTool = oSM.GetPart(0);
886 : }
887 : // Write style brush fore color
888 3 : if( poTool && poTool->GetType() == OGRSTCBrush )
889 : {
890 1 : OGRStyleBrush *poBrush = (OGRStyleBrush *) poTool;
891 : GBool bDefault;
892 :
893 1 : if( poBrush->ForeColor(bDefault) != NULL && !bDefault )
894 1 : WriteValue( 62, ColorStringToDXFColor( poBrush->ForeColor(bDefault) ) );
895 : }
896 3 : delete poTool;
897 :
898 : /* -------------------------------------------------------------------- */
899 : /* Handle a PEN tool to control drawing color and width. */
900 : /* Perhaps one day also dottedness, etc. */
901 : /* -------------------------------------------------------------------- */
902 : #ifdef notdef
903 : if( poTool && poTool->GetType() == OGRSTCPen )
904 : {
905 : OGRStylePen *poPen = (OGRStylePen *) poTool;
906 : GBool bDefault;
907 :
908 : if( poPen->Color(bDefault) != NULL && !bDefault )
909 : WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) );
910 :
911 : double dfWidthInMM = poPen->Width(bDefault);
912 :
913 : if( !bDefault )
914 : WriteValue( 370, (int) floor(dfWidthInMM * 100 + 0.5) );
915 : }
916 :
917 : /* -------------------------------------------------------------------- */
918 : /* Do we have a Linetype for the feature? */
919 : /* -------------------------------------------------------------------- */
920 : CPLString osLineType = poFeature->GetFieldAsString( "Linetype" );
921 :
922 : if( osLineType.size() > 0
923 : && (poDS->oHeaderDS.LookupLineType( osLineType ) != NULL
924 : || oNewLineTypes.count(osLineType) > 0 ) )
925 : {
926 : // Already define -> just reference it.
927 : WriteValue( 6, osLineType );
928 : }
929 : else if( poTool != NULL && poTool->GetType() == OGRSTCPen )
930 : {
931 : CPLString osDefinition = PrepareLineTypeDefinition( poFeature,
932 : poTool );
933 :
934 : if( osDefinition != "" && osLineType == "" )
935 : {
936 : // Is this definition already created and named?
937 : std::map<CPLString,CPLString>::iterator it;
938 :
939 : for( it = oNewLineTypes.begin();
940 : it != oNewLineTypes.end();
941 : it++ )
942 : {
943 : if( (*it).second == osDefinition )
944 : {
945 : osLineType = (*it).first;
946 : break;
947 : }
948 : }
949 :
950 : // create an automatic name for it.
951 : if( osLineType == "" )
952 : {
953 : do
954 : {
955 : osLineType.Printf( "AutoLineType-%d", nNextAutoID++ );
956 : }
957 : while( poDS->oHeaderDS.LookupLineType(osLineType) != NULL );
958 : }
959 : }
960 :
961 : // If it isn't already defined, add it now.
962 : if( osDefinition != "" && oNewLineTypes.count(osLineType) == 0 )
963 : {
964 : oNewLineTypes[osLineType] = osDefinition;
965 : WriteValue( 6, osLineType );
966 : }
967 : }
968 : delete poTool;
969 : #endif
970 :
971 : /* -------------------------------------------------------------------- */
972 : /* Process the loops (rings). */
973 : /* -------------------------------------------------------------------- */
974 3 : OGRPolygon *poPoly = (OGRPolygon *) poGeom;
975 :
976 3 : WriteValue( 91, poPoly->getNumInteriorRings() + 1 );
977 :
978 7 : for( int iRing = -1; iRing < poPoly->getNumInteriorRings(); iRing++ )
979 : {
980 : OGRLinearRing *poLR;
981 :
982 4 : if( iRing == -1 )
983 3 : poLR = poPoly->getExteriorRing();
984 : else
985 1 : poLR = poPoly->getInteriorRing( iRing );
986 :
987 4 : WriteValue( 92, 2 ); // Polyline
988 4 : WriteValue( 72, 0 ); // has bulge
989 4 : WriteValue( 73, 1 ); // is closed
990 4 : WriteValue( 93, poLR->getNumPoints() );
991 :
992 22 : for( int iVert = 0; iVert < poLR->getNumPoints(); iVert++ )
993 : {
994 18 : WriteValue( 10, poLR->getX(iVert) );
995 18 : WriteValue( 20, poLR->getY(iVert) );
996 : }
997 :
998 4 : WriteValue( 97, 0 ); // 0 source boundary objects
999 : }
1000 :
1001 3 : WriteValue( 75, 0 ); // hatch style = Hatch "odd parity" area (Normal style)
1002 3 : WriteValue( 76, 1 ); // hatch pattern type = predefined
1003 3 : WriteValue( 98, 0 ); // 0 seed points
1004 :
1005 3 : return OGRERR_NONE;
1006 :
1007 : #ifdef notdef
1008 : /* -------------------------------------------------------------------- */
1009 : /* Alternate unmaintained implementation as a polyline entity. */
1010 : /* -------------------------------------------------------------------- */
1011 : WriteValue( 0, "POLYLINE" );
1012 : WriteCore( poFeature );
1013 : WriteValue( 100, "AcDbEntity" );
1014 : WriteValue( 100, "AcDbPolyline" );
1015 : if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
1016 : WriteValue( 70, 1 );
1017 : else
1018 : WriteValue( 70, 0 );
1019 : WriteValue( 66, "1" );
1020 :
1021 : int iVert;
1022 :
1023 : for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
1024 : {
1025 : WriteValue( 0, "VERTEX" );
1026 : WriteValue( 8, "0" );
1027 : WriteValue( 10, poLS->getX(iVert) );
1028 : if( !WriteValue( 20, poLS->getY(iVert) ) )
1029 : return OGRERR_FAILURE;
1030 :
1031 : if( poLS->getGeometryType() == wkbLineString25D )
1032 : {
1033 : if( !WriteValue( 30, poLS->getZ(iVert) ) )
1034 : return OGRERR_FAILURE;
1035 : }
1036 : }
1037 :
1038 : WriteValue( 0, "SEQEND" );
1039 : WriteValue( 8, "0" );
1040 :
1041 : return OGRERR_NONE;
1042 : #endif
1043 : }
1044 :
1045 : /************************************************************************/
1046 : /* CreateFeature() */
1047 : /************************************************************************/
1048 :
1049 17 : OGRErr OGRDXFWriterLayer::CreateFeature( OGRFeature *poFeature )
1050 :
1051 : {
1052 17 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1053 17 : OGRwkbGeometryType eGType = wkbNone;
1054 :
1055 17 : if( poGeom != NULL )
1056 : {
1057 17 : if( !poGeom->IsEmpty() )
1058 : {
1059 17 : OGREnvelope sEnvelope;
1060 17 : poGeom->getEnvelope(&sEnvelope);
1061 17 : poDS->UpdateExtent(&sEnvelope);
1062 : }
1063 17 : eGType = wkbFlatten(poGeom->getGeometryType());
1064 : }
1065 :
1066 17 : if( eGType == wkbPoint )
1067 : {
1068 6 : const char *pszBlockName = poFeature->GetFieldAsString("BlockName");
1069 :
1070 : // we don't want to treat as a block ref if we are writing blocks layer
1071 10 : if( pszBlockName != NULL
1072 : && poDS->poBlocksLayer != NULL
1073 4 : && poFeature->GetDefnRef() == poDS->poBlocksLayer->GetLayerDefn())
1074 0 : pszBlockName = NULL;
1075 :
1076 : // We don't want to treat as a blocks ref if the block is not defined
1077 6 : if( pszBlockName
1078 : && poDS->oHeaderDS.LookupBlock(pszBlockName) == NULL )
1079 : {
1080 4 : if( poDS->poBlocksLayer == NULL
1081 : || poDS->poBlocksLayer->FindBlock(pszBlockName) == NULL )
1082 2 : pszBlockName = NULL;
1083 : }
1084 :
1085 6 : if( pszBlockName != NULL )
1086 4 : return WriteINSERT( poFeature );
1087 :
1088 3 : else if( poFeature->GetStyleString() != NULL
1089 1 : && EQUALN(poFeature->GetStyleString(),"LABEL",5) )
1090 1 : return WriteTEXT( poFeature );
1091 : else
1092 1 : return WritePOINT( poFeature );
1093 : }
1094 11 : else if( eGType == wkbLineString
1095 : || eGType == wkbMultiLineString )
1096 7 : return WritePOLYLINE( poFeature );
1097 :
1098 4 : else if( eGType == wkbPolygon
1099 : || eGType == wkbMultiPolygon )
1100 : {
1101 3 : if( bWriteHatch )
1102 3 : return WriteHATCH( poFeature );
1103 : else
1104 0 : return WritePOLYLINE( poFeature );
1105 : }
1106 :
1107 : // Explode geometry collections into multiple entities.
1108 1 : else if( eGType == wkbGeometryCollection )
1109 : {
1110 : OGRGeometryCollection *poGC = (OGRGeometryCollection *)
1111 1 : poFeature->StealGeometry();
1112 : int iGeom;
1113 :
1114 3 : for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ )
1115 : {
1116 2 : poFeature->SetGeometry( poGC->getGeometryRef(iGeom) );
1117 :
1118 2 : OGRErr eErr = CreateFeature( poFeature );
1119 :
1120 2 : if( eErr != OGRERR_NONE )
1121 0 : return eErr;
1122 :
1123 : }
1124 :
1125 1 : poFeature->SetGeometryDirectly( poGC );
1126 1 : return OGRERR_NONE;
1127 : }
1128 : else
1129 : {
1130 : CPLError( CE_Failure, CPLE_AppDefined,
1131 : "No known way to write feature with geometry '%s'.",
1132 0 : OGRGeometryTypeToName(eGType) );
1133 0 : return OGRERR_FAILURE;
1134 : }
1135 : }
1136 :
1137 : /************************************************************************/
1138 : /* ColorStringToDXFColor() */
1139 : /************************************************************************/
1140 :
1141 5 : int OGRDXFWriterLayer::ColorStringToDXFColor( const char *pszRGB )
1142 :
1143 : {
1144 : /* -------------------------------------------------------------------- */
1145 : /* Parse the RGB string. */
1146 : /* -------------------------------------------------------------------- */
1147 5 : if( pszRGB == NULL )
1148 0 : return -1;
1149 :
1150 5 : int nRed, nGreen, nBlue, nTransparency = 255;
1151 :
1152 : int nCount = sscanf(pszRGB,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,
1153 5 : &nTransparency);
1154 :
1155 5 : if (nCount < 3 )
1156 0 : return -1;
1157 :
1158 : /* -------------------------------------------------------------------- */
1159 : /* Find near color in DXF palette. */
1160 : /* -------------------------------------------------------------------- */
1161 5 : const unsigned char *pabyDXFColors = ACGetColorTable();
1162 : int i;
1163 5 : int nMinDist = 768;
1164 5 : int nBestColor = -1;
1165 :
1166 1280 : for( i = 1; i < 256; i++ )
1167 : {
1168 1275 : int nDist = ABS(nRed - pabyDXFColors[i*3+0])
1169 1275 : + ABS(nGreen - pabyDXFColors[i*3+1])
1170 3825 : + ABS(nBlue - pabyDXFColors[i*3+2]);
1171 :
1172 1275 : if( nDist < nMinDist )
1173 : {
1174 8 : nBestColor = i;
1175 8 : nMinDist = nDist;
1176 : }
1177 : }
1178 :
1179 5 : return nBestColor;
1180 : }
|