1 : /******************************************************************************
2 : * $Id: ogrdxfwriterlayer.cpp 19643 2010-05-08 21:56:18Z 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 19643 2010-05-08 21:56:18Z rouault $");
37 :
38 : #ifndef PI
39 : #define PI 3.14159265358979323846
40 : #endif
41 :
42 : /************************************************************************/
43 : /* OGRDXFWriterLayer() */
44 : /************************************************************************/
45 :
46 1 : OGRDXFWriterLayer::OGRDXFWriterLayer( FILE *fp )
47 :
48 : {
49 1 : this->fp = fp;
50 :
51 1 : nNextFID = 80;
52 :
53 1 : poFeatureDefn = new OGRFeatureDefn( "entities" );
54 1 : poFeatureDefn->Reference();
55 :
56 1 : OGRFieldDefn oLayerField( "Layer", OFTString );
57 1 : poFeatureDefn->AddFieldDefn( &oLayerField );
58 :
59 1 : OGRFieldDefn oClassField( "SubClasses", OFTString );
60 1 : poFeatureDefn->AddFieldDefn( &oClassField );
61 :
62 1 : OGRFieldDefn oExtendedField( "ExtendedEntity", OFTString );
63 1 : poFeatureDefn->AddFieldDefn( &oExtendedField );
64 :
65 1 : OGRFieldDefn oLinetypeField( "Linetype", OFTString );
66 1 : poFeatureDefn->AddFieldDefn( &oLinetypeField );
67 :
68 1 : OGRFieldDefn oEntityHandleField( "EntityHandle", OFTString );
69 1 : poFeatureDefn->AddFieldDefn( &oEntityHandleField );
70 :
71 1 : OGRFieldDefn oTextField( "Text", OFTString );
72 1 : poFeatureDefn->AddFieldDefn( &oTextField );
73 1 : }
74 :
75 : /************************************************************************/
76 : /* ~OGRDXFWriterLayer() */
77 : /************************************************************************/
78 :
79 1 : OGRDXFWriterLayer::~OGRDXFWriterLayer()
80 :
81 : {
82 1 : if( poFeatureDefn )
83 1 : poFeatureDefn->Release();
84 1 : }
85 :
86 : /************************************************************************/
87 : /* TestCapability() */
88 : /************************************************************************/
89 :
90 0 : int OGRDXFWriterLayer::TestCapability( const char * pszCap )
91 :
92 : {
93 0 : if( EQUAL(pszCap,OLCSequentialWrite) )
94 0 : return TRUE;
95 : else
96 0 : return FALSE;
97 : }
98 :
99 : /************************************************************************/
100 : /* CreateField() */
101 : /* */
102 : /* This is really a dummy as our fields are precreated. */
103 : /************************************************************************/
104 :
105 : OGRErr OGRDXFWriterLayer::CreateField( OGRFieldDefn *poField,
106 0 : int bApproxOK )
107 :
108 : {
109 0 : if( poFeatureDefn->GetFieldIndex(poField->GetNameRef()) >= 0
110 : && bApproxOK )
111 0 : return OGRERR_NONE;
112 :
113 : CPLError( CE_Failure, CPLE_AppDefined,
114 0 : "DXF layer does not support arbitrary field creation." );
115 :
116 0 : return OGRERR_FAILURE;
117 : }
118 :
119 : /************************************************************************/
120 : /* WriteValue() */
121 : /************************************************************************/
122 :
123 10 : int OGRDXFWriterLayer::WriteValue( int nCode, const char *pszValue )
124 :
125 : {
126 10 : CPLString osLinePair;
127 :
128 10 : osLinePair.Printf( "%3d\n", nCode );
129 :
130 10 : if( strlen(pszValue) < 255 )
131 10 : osLinePair += pszValue;
132 : else
133 0 : osLinePair.append( pszValue, 255 );
134 :
135 10 : osLinePair += "\n";
136 :
137 : return VSIFWriteL( osLinePair.c_str(),
138 10 : 1, osLinePair.size(), fp ) == osLinePair.size();
139 : }
140 :
141 : /************************************************************************/
142 : /* WriteValue() */
143 : /************************************************************************/
144 :
145 4 : int OGRDXFWriterLayer::WriteValue( int nCode, int nValue )
146 :
147 : {
148 4 : CPLString osLinePair;
149 :
150 4 : osLinePair.Printf( "%3d\n%d\n", nCode, nValue );
151 :
152 : return VSIFWriteL( osLinePair.c_str(),
153 4 : 1, osLinePair.size(), fp ) == osLinePair.size();
154 : }
155 :
156 : /************************************************************************/
157 : /* WriteValue() */
158 : /************************************************************************/
159 :
160 12 : int OGRDXFWriterLayer::WriteValue( int nCode, double dfValue )
161 :
162 : {
163 : char szLinePair[64];
164 :
165 12 : snprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue );
166 12 : char* pszComma = strchr(szLinePair, ',');
167 12 : if (pszComma)
168 0 : *pszComma = '.';
169 12 : size_t nLen = strlen(szLinePair);
170 :
171 : return VSIFWriteL( szLinePair,
172 12 : 1, nLen, fp ) == nLen;
173 : }
174 :
175 : /************************************************************************/
176 : /* WriteCore() */
177 : /* */
178 : /* Write core fields common to all sorts of elements. */
179 : /************************************************************************/
180 :
181 2 : OGRErr OGRDXFWriterLayer::WriteCore( OGRFeature *poFeature )
182 :
183 : {
184 : /* -------------------------------------------------------------------- */
185 : /* Write out an entity id. I'm not sure why this is critical, */
186 : /* but it seems that VoloView will just quietly fail to open */
187 : /* dxf files without entity ids set on most/all entities. */
188 : /* Also, for reasons I don't understand these ids seem to have */
189 : /* to start somewhere around 0x50 hex (80 decimal). */
190 : /* -------------------------------------------------------------------- */
191 : char szEntityID[16];
192 :
193 2 : sprintf( szEntityID, "%X", nNextFID++ );
194 2 : WriteValue( 5, szEntityID );
195 :
196 : /* -------------------------------------------------------------------- */
197 : /* For now we assign everything to the default layer - layer */
198 : /* "0". */
199 : /* -------------------------------------------------------------------- */
200 2 : WriteValue( 8, "0" ); // layer
201 :
202 2 : return OGRERR_NONE;
203 : }
204 :
205 : /************************************************************************/
206 : /* WritePOINT() */
207 : /************************************************************************/
208 :
209 0 : OGRErr OGRDXFWriterLayer::WritePOINT( OGRFeature *poFeature )
210 :
211 : {
212 0 : WriteValue( 0, "POINT" );
213 0 : WriteCore( poFeature );
214 0 : WriteValue( 100, "AcDbEntity" );
215 0 : WriteValue( 100, "AcDbPoint" );
216 :
217 0 : OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef();
218 :
219 0 : WriteValue( 10, poPoint->getX() );
220 0 : if( !WriteValue( 20, poPoint->getY() ) )
221 0 : return OGRERR_FAILURE;
222 :
223 0 : if( poPoint->getGeometryType() == wkbPoint25D )
224 : {
225 0 : if( !WriteValue( 30, poPoint->getZ() ) )
226 0 : return OGRERR_FAILURE;
227 : }
228 :
229 0 : return OGRERR_NONE;
230 : }
231 :
232 : /************************************************************************/
233 : /* WriteTEXT() */
234 : /************************************************************************/
235 :
236 0 : OGRErr OGRDXFWriterLayer::WriteTEXT( OGRFeature *poFeature )
237 :
238 : {
239 0 : WriteValue( 0, "MTEXT" );
240 0 : WriteCore( poFeature );
241 0 : WriteValue( 100, "AcDbEntity" );
242 0 : WriteValue( 100, "AcDbMText" );
243 :
244 : /* -------------------------------------------------------------------- */
245 : /* Do we have styling information? */
246 : /* -------------------------------------------------------------------- */
247 0 : OGRStyleTool *poTool = NULL;
248 0 : OGRStyleMgr oSM;
249 :
250 0 : if( poFeature->GetStyleString() != NULL )
251 : {
252 0 : oSM.InitFromFeature( poFeature );
253 :
254 0 : if( oSM.GetPartCount() > 0 )
255 0 : poTool = oSM.GetPart(0);
256 : }
257 :
258 : /* ==================================================================== */
259 : /* Process the LABEL tool. */
260 : /* ==================================================================== */
261 0 : if( poTool && poTool->GetType() == OGRSTCLabel )
262 : {
263 0 : OGRStyleLabel *poLabel = (OGRStyleLabel *) poTool;
264 : GBool bDefault;
265 :
266 : /* -------------------------------------------------------------------- */
267 : /* Color */
268 : /* -------------------------------------------------------------------- */
269 0 : if( poLabel->ForeColor(bDefault) != NULL && !bDefault )
270 : WriteValue( 62, ColorStringToDXFColor(
271 0 : poLabel->ForeColor(bDefault) ) );
272 :
273 : /* -------------------------------------------------------------------- */
274 : /* Angle */
275 : /* -------------------------------------------------------------------- */
276 0 : double dfAngle = poLabel->Angle(bDefault);
277 :
278 0 : if( !bDefault )
279 0 : WriteValue( 50, dfAngle * (PI/180.0) );
280 :
281 : /* -------------------------------------------------------------------- */
282 : /* Height - We need to fetch this in georeferenced units - I'm */
283 : /* doubt the default translation mechanism will be much good. */
284 : /* -------------------------------------------------------------------- */
285 0 : poTool->SetUnit( OGRSTUGround );
286 0 : double dfHeight = poLabel->Size(bDefault);
287 :
288 0 : if( !bDefault )
289 0 : WriteValue( 40, dfHeight );
290 :
291 : /* -------------------------------------------------------------------- */
292 : /* Anchor / Attachment Point */
293 : /* -------------------------------------------------------------------- */
294 0 : int nAnchor = poLabel->Anchor(bDefault);
295 :
296 0 : if( !bDefault )
297 : {
298 : const static int anAnchorMap[] =
299 : { -1, 7, 8, 9, 4, 5, 6, 1, 2, 3, 7, 8, 9 };
300 :
301 0 : if( nAnchor > 0 && nAnchor < 13 )
302 0 : WriteValue( 71, anAnchorMap[nAnchor] );
303 : }
304 :
305 : /* -------------------------------------------------------------------- */
306 : /* Text - split into distinct lines. */
307 : /* -------------------------------------------------------------------- */
308 0 : const char *pszText = poLabel->TextString( bDefault );
309 :
310 0 : if( pszText != NULL && !bDefault )
311 : {
312 : int iLine;
313 : char **papszLines = CSLTokenizeStringComplex(
314 0 : pszText, "\n", FALSE, TRUE );
315 :
316 0 : for( iLine = 0;
317 : papszLines != NULL && papszLines[iLine] != NULL;
318 : iLine++ )
319 : {
320 0 : if( iLine == 0 )
321 0 : WriteValue( 1, papszLines[iLine] );
322 : else
323 0 : WriteValue( 3, papszLines[iLine] );
324 : }
325 0 : CSLDestroy( papszLines );
326 : }
327 : }
328 :
329 0 : delete poTool;
330 :
331 : /* -------------------------------------------------------------------- */
332 : /* Write the location. */
333 : /* -------------------------------------------------------------------- */
334 0 : OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef();
335 :
336 0 : WriteValue( 10, poPoint->getX() );
337 0 : if( !WriteValue( 20, poPoint->getY() ) )
338 0 : return OGRERR_FAILURE;
339 :
340 0 : if( poPoint->getGeometryType() == wkbPoint25D )
341 : {
342 0 : if( !WriteValue( 30, poPoint->getZ() ) )
343 0 : return OGRERR_FAILURE;
344 : }
345 :
346 0 : return OGRERR_NONE;
347 :
348 : }
349 :
350 : /************************************************************************/
351 : /* WritePOLYLINE() */
352 : /************************************************************************/
353 :
354 : OGRErr OGRDXFWriterLayer::WritePOLYLINE( OGRFeature *poFeature,
355 3 : OGRGeometry *poGeom )
356 :
357 : {
358 : /* -------------------------------------------------------------------- */
359 : /* For now we handle multilinestrings by writing a series of */
360 : /* entities. */
361 : /* -------------------------------------------------------------------- */
362 3 : if( poGeom == NULL )
363 2 : poGeom = poFeature->GetGeometryRef();
364 :
365 3 : if ( poGeom->IsEmpty() )
366 : {
367 0 : return OGRERR_NONE;
368 : }
369 :
370 3 : if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon
371 : || wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString )
372 : {
373 0 : OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom;
374 : int iGeom;
375 0 : OGRErr eErr = OGRERR_NONE;
376 :
377 0 : for( iGeom = 0;
378 : eErr == OGRERR_NONE && iGeom < poGC->getNumGeometries();
379 : iGeom++ )
380 : {
381 0 : eErr = WritePOLYLINE( poFeature, poGC->getGeometryRef( iGeom ) );
382 : }
383 :
384 0 : return eErr;
385 : }
386 :
387 : /* -------------------------------------------------------------------- */
388 : /* Polygons are written with on entity per ring. */
389 : /* -------------------------------------------------------------------- */
390 3 : if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
391 : {
392 1 : OGRPolygon *poPoly = (OGRPolygon *) poGeom;
393 : int iGeom;
394 : OGRErr eErr;
395 :
396 1 : eErr = WritePOLYLINE( poFeature, poPoly->getExteriorRing() );
397 1 : for( iGeom = 0;
398 : eErr == OGRERR_NONE && iGeom < poPoly->getNumInteriorRings();
399 : iGeom++ )
400 : {
401 0 : eErr = WritePOLYLINE( poFeature, poPoly->getInteriorRing(iGeom) );
402 : }
403 :
404 1 : return eErr;
405 : }
406 :
407 : /* -------------------------------------------------------------------- */
408 : /* Do we now have a geometry we can work with? */
409 : /* -------------------------------------------------------------------- */
410 2 : if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
411 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
412 :
413 2 : OGRLineString *poLS = (OGRLineString *) poGeom;
414 :
415 : /* -------------------------------------------------------------------- */
416 : /* Write as a lightweight polygon. */
417 : /* -------------------------------------------------------------------- */
418 2 : WriteValue( 0, "LWPOLYLINE" );
419 2 : WriteCore( poFeature );
420 2 : WriteValue( 100, "AcDbEntity" );
421 2 : WriteValue( 100, "AcDbPolyline" );
422 2 : if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
423 1 : WriteValue( 70, 1 );
424 : else
425 1 : WriteValue( 70, 0 );
426 2 : WriteValue( 90, poLS->getNumPoints() );
427 :
428 : /* -------------------------------------------------------------------- */
429 : /* Do we have styling information? */
430 : /* -------------------------------------------------------------------- */
431 2 : OGRStyleTool *poTool = NULL;
432 2 : OGRStyleMgr oSM;
433 :
434 2 : if( poFeature->GetStyleString() != NULL )
435 : {
436 0 : oSM.InitFromFeature( poFeature );
437 :
438 0 : if( oSM.GetPartCount() > 0 )
439 0 : poTool = oSM.GetPart(0);
440 : }
441 :
442 : /* -------------------------------------------------------------------- */
443 : /* Handle a PEN tool to control drawing color and width. */
444 : /* Perhaps one day also dottedness, etc. */
445 : /* -------------------------------------------------------------------- */
446 2 : if( poTool && poTool->GetType() == OGRSTCPen )
447 : {
448 0 : OGRStylePen *poPen = (OGRStylePen *) poTool;
449 : GBool bDefault;
450 :
451 0 : if( poPen->Color(bDefault) != NULL && !bDefault )
452 0 : WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) );
453 :
454 0 : double dfWidthInMM = poPen->Width(bDefault);
455 :
456 0 : if( !bDefault )
457 0 : WriteValue( 370, (int) floor(dfWidthInMM * 100 + 0.5) );
458 : }
459 :
460 2 : delete poTool;
461 :
462 : /* -------------------------------------------------------------------- */
463 : /* Write the vertices */
464 : /* -------------------------------------------------------------------- */
465 : int iVert;
466 :
467 8 : for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
468 : {
469 6 : WriteValue( 10, poLS->getX(iVert) );
470 6 : if( !WriteValue( 20, poLS->getY(iVert) ) )
471 2 : return OGRERR_FAILURE;
472 :
473 6 : if( poLS->getGeometryType() == wkbLineString25D )
474 : {
475 0 : if( !WriteValue( 38, poLS->getZ(iVert) ) )
476 0 : return OGRERR_FAILURE;
477 : }
478 : }
479 :
480 2 : return OGRERR_NONE;
481 :
482 : #ifdef notdef
483 : /* -------------------------------------------------------------------- */
484 : /* Alternate unmaintained implementation as a polyline entity. */
485 : /* -------------------------------------------------------------------- */
486 : WriteValue( 0, "POLYLINE" );
487 : WriteCore( poFeature );
488 : WriteValue( 100, "AcDbEntity" );
489 : WriteValue( 100, "AcDbPolyline" );
490 : if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
491 : WriteValue( 70, 1 );
492 : else
493 : WriteValue( 70, 0 );
494 : WriteValue( 66, "1" );
495 :
496 : int iVert;
497 :
498 : for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
499 : {
500 : WriteValue( 0, "VERTEX" );
501 : WriteValue( 8, "0" );
502 : WriteValue( 10, poLS->getX(iVert) );
503 : if( !WriteValue( 20, poLS->getY(iVert) ) )
504 : return OGRERR_FAILURE;
505 :
506 : if( poLS->getGeometryType() == wkbLineString25D )
507 : {
508 : if( !WriteValue( 30, poLS->getZ(iVert) ) )
509 : return OGRERR_FAILURE;
510 : }
511 : }
512 :
513 : WriteValue( 0, "SEQEND" );
514 : WriteValue( 8, "0" );
515 :
516 : return OGRERR_NONE;
517 : #endif
518 : }
519 :
520 : /************************************************************************/
521 : /* CreateFeature() */
522 : /************************************************************************/
523 :
524 2 : OGRErr OGRDXFWriterLayer::CreateFeature( OGRFeature *poFeature )
525 :
526 : {
527 2 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
528 2 : OGRwkbGeometryType eGType = wkbNone;
529 :
530 2 : if( poGeom != NULL )
531 2 : eGType = wkbFlatten(poGeom->getGeometryType());
532 :
533 2 : if( eGType == wkbPoint )
534 : {
535 0 : if( poFeature->GetStyleString() != NULL
536 : && EQUALN(poFeature->GetStyleString(),"LABEL",5) )
537 0 : return WriteTEXT( poFeature );
538 : else
539 0 : return WritePOINT( poFeature );
540 : }
541 2 : else if( eGType == wkbLineString
542 : || eGType == wkbMultiLineString
543 : || eGType == wkbPolygon
544 : || eGType == wkbMultiPolygon )
545 2 : return WritePOLYLINE( poFeature );
546 : else
547 : {
548 : CPLError( CE_Failure, CPLE_AppDefined,
549 : "No known way to write feature with geometry '%s'.",
550 0 : OGRGeometryTypeToName(eGType) );
551 0 : return OGRERR_FAILURE;
552 : }
553 :
554 : return OGRERR_NONE;
555 : }
556 :
557 : /************************************************************************/
558 : /* ColorStringToDXFColor() */
559 : /************************************************************************/
560 :
561 0 : int OGRDXFWriterLayer::ColorStringToDXFColor( const char *pszRGB )
562 :
563 : {
564 : /* -------------------------------------------------------------------- */
565 : /* Parse the RGB string. */
566 : /* -------------------------------------------------------------------- */
567 0 : if( pszRGB == NULL )
568 0 : return -1;
569 :
570 0 : int nRed, nGreen, nBlue, nTransparency = 255;
571 :
572 : int nCount = sscanf(pszRGB,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,
573 0 : &nTransparency);
574 :
575 0 : if (nCount < 3 )
576 0 : return -1;
577 :
578 : /* -------------------------------------------------------------------- */
579 : /* Find near color in DXF palette. */
580 : /* -------------------------------------------------------------------- */
581 0 : const unsigned char *pabyDXFColors = OGRDXFDriver::GetDXFColorTable();
582 : int i;
583 0 : int nMinDist = 768;
584 0 : int nBestColor = -1;
585 :
586 0 : for( i = 1; i < 256; i++ )
587 : {
588 : int nDist = ABS(nRed - pabyDXFColors[i*3+0])
589 : + ABS(nGreen - pabyDXFColors[i*3+1])
590 0 : + ABS(nBlue - pabyDXFColors[i*3+2]);
591 :
592 0 : if( nDist < nMinDist )
593 : {
594 0 : nBestColor = i;
595 0 : nMinDist = nDist;
596 : }
597 : }
598 :
599 0 : return nBestColor;
600 : }
|