source: trunk/gdal/ogr/ogrsf_frmts/dxf/ogrdxfwriterlayer.cpp

Last change on this file was 33713, checked in by goatbar, 2 months ago

Partial stripping of trailing white space

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