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