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