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

Last change on this file was 34444, checked in by goatbar, 40 hours ago

cleanup trailing white space in ogrsf_frmts

  • Property svn:keywords set to Author Date Id Revision
File size: 42.3 KB
Line 
1/******************************************************************************
2 * $Id: ogrdxfwriterlayer.cpp 34444 2016-06-25 22:22:46Z 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 * 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 34444 2016-06-25 22:22:46Z rouault $");
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        CPLString osSanitizedLayer(pszLayer);
225        // Replaced restricted characters with underscore
226        // See http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
227        const char achForbiddenChars[] = { '<', '>', '/', '\\', '"', ':', ';', '?', '*', '|', '=', '\'' };
228        for( size_t i = 0; i < CPL_ARRAYSIZE(achForbiddenChars); ++i )
229        {
230            osSanitizedLayer.replaceAll( achForbiddenChars[i], '_' );
231        }
232
233        // also remove newline characters (#15067)
234        osSanitizedLayer.replaceAll( "\r\n", "_" );
235        osSanitizedLayer.replaceAll( '\r', '_' );
236        osSanitizedLayer.replaceAll( '\n', '_' );
237
238        const char *pszExists =
239            poDS->oHeaderDS.LookupLayerProperty( osSanitizedLayer, "Exists" );
240        if( (pszExists == NULL || strlen(pszExists) == 0)
241            && CSLFindString( poDS->papszLayersToCreate, osSanitizedLayer ) == -1 )
242        {
243            poDS->papszLayersToCreate =
244                CSLAddString( poDS->papszLayersToCreate, osSanitizedLayer );
245        }
246
247        WriteValue( 8, osSanitizedLayer );
248    }
249
250    return OGRERR_NONE;
251}
252
253/************************************************************************/
254/*                            WriteINSERT()                             */
255/************************************************************************/
256
257OGRErr OGRDXFWriterLayer::WriteINSERT( OGRFeature *poFeature )
258
259{
260    WriteValue( 0, "INSERT" );
261    WriteCore( poFeature );
262    WriteValue( 100, "AcDbEntity" );
263    WriteValue( 100, "AcDbBlockReference" );
264    WriteValue( 2, poFeature->GetFieldAsString("BlockName") );
265
266    // Write style symbol color
267    OGRStyleTool *poTool = NULL;
268    OGRStyleMgr oSM;
269    if( poFeature->GetStyleString() != NULL )
270    {
271        oSM.InitFromFeature( poFeature );
272
273        if( oSM.GetPartCount() > 0 )
274            poTool = oSM.GetPart(0);
275    }
276    if( poTool && poTool->GetType() == OGRSTCSymbol )
277    {
278        OGRStyleSymbol *poSymbol = (OGRStyleSymbol *) poTool;
279        GBool  bDefault;
280
281        if( poSymbol->Color(bDefault) != NULL && !bDefault )
282            WriteValue( 62, ColorStringToDXFColor( poSymbol->Color(bDefault) ) );
283    }
284    delete poTool;
285
286/* -------------------------------------------------------------------- */
287/*      Write location.                                                 */
288/* -------------------------------------------------------------------- */
289    OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef();
290
291    WriteValue( 10, poPoint->getX() );
292    if( !WriteValue( 20, poPoint->getY() ) )
293        return OGRERR_FAILURE;
294
295    if( poPoint->getGeometryType() == wkbPoint25D )
296    {
297        if( !WriteValue( 30, poPoint->getZ() ) )
298            return OGRERR_FAILURE;
299    }
300
301/* -------------------------------------------------------------------- */
302/*      Write scaling.                                                  */
303/* -------------------------------------------------------------------- */
304    int nScaleCount;
305    const double *padfScale =
306        poFeature->GetFieldAsDoubleList( "BlockScale", &nScaleCount );
307
308    if( nScaleCount == 3 )
309    {
310        WriteValue( 41, padfScale[0] );
311        WriteValue( 42, padfScale[1] );
312        WriteValue( 43, padfScale[2] );
313    }
314
315/* -------------------------------------------------------------------- */
316/*      Write rotation.                                                 */
317/* -------------------------------------------------------------------- */
318    double dfAngle = poFeature->GetFieldAsDouble( "BlockAngle" );
319
320    if( dfAngle != 0.0 )
321    {
322        WriteValue( 50, dfAngle ); // degrees
323    }
324
325    return OGRERR_NONE;
326}
327
328/************************************************************************/
329/*                             WritePOINT()                             */
330/************************************************************************/
331
332OGRErr OGRDXFWriterLayer::WritePOINT( OGRFeature *poFeature )
333
334{
335    WriteValue( 0, "POINT" );
336    WriteCore( poFeature );
337    WriteValue( 100, "AcDbEntity" );
338    WriteValue( 100, "AcDbPoint" );
339
340    // Write style pen color
341    OGRStyleTool *poTool = NULL;
342    OGRStyleMgr oSM;
343    if( poFeature->GetStyleString() != NULL )
344    {
345        oSM.InitFromFeature( poFeature );
346
347        if( oSM.GetPartCount() > 0 )
348            poTool = oSM.GetPart(0);
349    }
350    if( poTool && poTool->GetType() == OGRSTCPen )
351    {
352        OGRStylePen *poPen = (OGRStylePen *) poTool;
353        GBool  bDefault;
354
355        if( poPen->Color(bDefault) != NULL && !bDefault )
356            WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) );
357    }
358    delete poTool;
359
360    OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef();
361
362    WriteValue( 10, poPoint->getX() );
363    if( !WriteValue( 20, poPoint->getY() ) )
364        return OGRERR_FAILURE;
365
366    if( poPoint->getGeometryType() == wkbPoint25D )
367    {
368        if( !WriteValue( 30, poPoint->getZ() ) )
369            return OGRERR_FAILURE;
370    }
371
372    return OGRERR_NONE;
373}
374
375/************************************************************************/
376/*                             TextEscape()                             */
377/*                                                                      */
378/*      Translate UTF8 to Win1252 and escape special characters like    */
379/*      newline and space with DXF style escapes.  Note that            */
380/*      non-win1252 unicode characters are translated using the         */
381/*      unicode escape sequence.                                        */
382/************************************************************************/
383
384CPLString OGRDXFWriterLayer::TextEscape( const char *pszInput )
385
386{
387    CPLString osResult;
388    wchar_t *panInput = CPLRecodeToWChar( pszInput,
389                                          CPL_ENC_UTF8,
390                                          CPL_ENC_UCS2 );
391    int i;
392
393
394    for( i = 0; panInput[i] != 0; i++ )
395    {
396        if( panInput[i] == '\n' )
397            osResult += "\\P";
398        else if( panInput[i] == ' ' )
399            osResult += "\\~";
400        else if( panInput[i] == '\\' )
401            osResult += "\\\\";
402        else if( panInput[i] > 255 )
403        {
404            CPLString osUnicode;
405            osUnicode.Printf( "\\U+%04x", (int) panInput[i] );
406            osResult += osUnicode;
407        }
408        else
409            osResult += (char) panInput[i];
410    }
411
412    CPLFree(panInput);
413
414    return osResult;
415}
416
417/************************************************************************/
418/*                             WriteTEXT()                              */
419/************************************************************************/
420
421OGRErr OGRDXFWriterLayer::WriteTEXT( OGRFeature *poFeature )
422
423{
424    WriteValue( 0, "MTEXT" );
425    WriteCore( poFeature );
426    WriteValue( 100, "AcDbEntity" );
427    WriteValue( 100, "AcDbMText" );
428
429/* -------------------------------------------------------------------- */
430/*      Do we have styling information?                                 */
431/* -------------------------------------------------------------------- */
432    OGRStyleTool *poTool = NULL;
433    OGRStyleMgr oSM;
434
435    if( poFeature->GetStyleString() != NULL )
436    {
437        oSM.InitFromFeature( poFeature );
438
439        if( oSM.GetPartCount() > 0 )
440            poTool = oSM.GetPart(0);
441    }
442
443/* ==================================================================== */
444/*      Process the LABEL tool.                                         */
445/* ==================================================================== */
446    if( poTool && poTool->GetType() == OGRSTCLabel )
447    {
448        OGRStyleLabel *poLabel = (OGRStyleLabel *) poTool;
449        GBool  bDefault;
450
451/* -------------------------------------------------------------------- */
452/*      Color                                                           */
453/* -------------------------------------------------------------------- */
454        if( poLabel->ForeColor(bDefault) != NULL && !bDefault )
455            WriteValue( 62, ColorStringToDXFColor(
456                            poLabel->ForeColor(bDefault) ) );
457
458/* -------------------------------------------------------------------- */
459/*      Angle                                                           */
460/* -------------------------------------------------------------------- */
461        double dfAngle = poLabel->Angle(bDefault);
462
463        // The DXF2000 reference says this is in radians, but in files
464        // I see it seems to be in degrees. Perhaps this is version dependent?
465        if( !bDefault )
466            WriteValue( 50, dfAngle );
467
468/* -------------------------------------------------------------------- */
469/*      Height - We need to fetch this in georeferenced units - I'm     */
470/*      doubt the default translation mechanism will be much good.      */
471/* -------------------------------------------------------------------- */
472        poTool->SetUnit( OGRSTUGround );
473        double dfHeight = poLabel->Size(bDefault);
474
475        if( !bDefault )
476            WriteValue( 40, dfHeight );
477
478/* -------------------------------------------------------------------- */
479/*      Anchor / Attachment Point                                       */
480/* -------------------------------------------------------------------- */
481        int nAnchor = poLabel->Anchor(bDefault);
482
483        if( !bDefault )
484        {
485            const static int anAnchorMap[] =
486                { -1, 7, 8, 9, 4, 5, 6, 1, 2, 3, 7, 8, 9 };
487
488            if( nAnchor > 0 && nAnchor < 13 )
489                WriteValue( 71, anAnchorMap[nAnchor] );
490        }
491
492/* -------------------------------------------------------------------- */
493/*      Escape the text, and convert to ISO8859.                        */
494/* -------------------------------------------------------------------- */
495        const char *pszText = poLabel->TextString( bDefault );
496
497        if( pszText != NULL && !bDefault )
498        {
499            CPLString osEscaped = TextEscape( pszText );
500            WriteValue( 1, osEscaped );
501        }
502    }
503
504    delete poTool;
505
506/* -------------------------------------------------------------------- */
507/*      Write the location.                                             */
508/* -------------------------------------------------------------------- */
509    OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef();
510
511    WriteValue( 10, poPoint->getX() );
512    if( !WriteValue( 20, poPoint->getY() ) )
513        return OGRERR_FAILURE;
514
515    if( poPoint->getGeometryType() == wkbPoint25D )
516    {
517        if( !WriteValue( 30, poPoint->getZ() ) )
518            return OGRERR_FAILURE;
519    }
520
521    return OGRERR_NONE;
522
523}
524
525/************************************************************************/
526/*                     PrepareLineTypeDefinition()                      */
527/************************************************************************/
528CPLString
529OGRDXFWriterLayer::PrepareLineTypeDefinition( CPL_UNUSED OGRFeature *poFeature,
530                                              OGRStyleTool *poTool )
531{
532    CPLString osDef;
533    OGRStylePen *poPen = (OGRStylePen *) poTool;
534    GBool  bDefault;
535    const char *pszPattern;
536
537/* -------------------------------------------------------------------- */
538/*      Fetch pattern.                                                  */
539/* -------------------------------------------------------------------- */
540    pszPattern = poPen->Pattern( bDefault );
541    if( bDefault || strlen(pszPattern) == 0 )
542        return "";
543
544/* -------------------------------------------------------------------- */
545/*      Split into pen up / pen down bits.                              */
546/* -------------------------------------------------------------------- */
547    char **papszTokens = CSLTokenizeString(pszPattern);
548    int i;
549    double dfTotalLength = 0;
550
551    for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++ )
552    {
553        const char *pszToken = papszTokens[i];
554        const char *pszUnit;
555        CPLString osAmount;
556        CPLString osDXFEntry;
557
558        // Split amount and unit.
559        for( pszUnit = pszToken;
560             strchr( "0123456789.", *pszUnit) != NULL;
561             pszUnit++ ) {}
562
563        osAmount.assign(pszToken,(int) (pszUnit-pszToken));
564
565        // If the unit is other than 'g' we really should be trying to
566        // do some type of transformation - but what to do?  Pretty hard.
567
568        // Even entries are "pen down" represented as negative in DXF.
569        if( i%2 == 0 )
570            osDXFEntry.Printf( " 49\n-%s\n 74\n0\n", osAmount.c_str() );
571        else
572            osDXFEntry.Printf( " 49\n%s\n 74\n0\n", osAmount.c_str() );
573
574        osDef += osDXFEntry;
575
576        dfTotalLength += CPLAtof(osAmount);
577    }
578
579/* -------------------------------------------------------------------- */
580/*      Prefix 73 and 40 items to the definition.                       */
581/* -------------------------------------------------------------------- */
582    CPLString osPrefix;
583
584    osPrefix.Printf( " 73\n%d\n 40\n%.6g\n",
585                     CSLCount(papszTokens),
586                     dfTotalLength );
587    osDef = osPrefix + osDef;
588
589    CSLDestroy( papszTokens );
590
591    return osDef;
592}
593
594/************************************************************************/
595/*                           WritePOLYLINE()                            */
596/************************************************************************/
597
598OGRErr OGRDXFWriterLayer::WritePOLYLINE( OGRFeature *poFeature,
599                                         OGRGeometry *poGeom )
600
601{
602/* -------------------------------------------------------------------- */
603/*      For now we handle multilinestrings by writing a series of       */
604/*      entities.                                                       */
605/* -------------------------------------------------------------------- */
606    if( poGeom == NULL )
607        poGeom = poFeature->GetGeometryRef();
608
609    if ( poGeom->IsEmpty() )
610    {
611        return OGRERR_NONE;
612    }
613
614    if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon
615        || wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString )
616    {
617        OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom;
618        int iGeom;
619        OGRErr eErr = OGRERR_NONE;
620
621        for( iGeom = 0;
622             eErr == OGRERR_NONE && iGeom < poGC->getNumGeometries();
623             iGeom++ )
624        {
625            eErr = WritePOLYLINE( poFeature, poGC->getGeometryRef( iGeom ) );
626        }
627
628        return eErr;
629    }
630
631/* -------------------------------------------------------------------- */
632/*      Polygons are written with on entity per ring.                   */
633/* -------------------------------------------------------------------- */
634    if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
635    {
636        OGRPolygon *poPoly = (OGRPolygon *) poGeom;
637        int iGeom;
638        OGRErr eErr;
639
640        eErr = WritePOLYLINE( poFeature, poPoly->getExteriorRing() );
641        for( iGeom = 0;
642             eErr == OGRERR_NONE && iGeom < poPoly->getNumInteriorRings();
643             iGeom++ )
644        {
645            eErr = WritePOLYLINE( poFeature, poPoly->getInteriorRing(iGeom) );
646        }
647
648        return eErr;
649    }
650
651/* -------------------------------------------------------------------- */
652/*      Do we now have a geometry we can work with?                     */
653/* -------------------------------------------------------------------- */
654    if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
655        return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
656
657    OGRLineString *poLS = (OGRLineString *) poGeom;
658
659/* -------------------------------------------------------------------- */
660/*      Write as a lightweight polygon,                                 */
661/*       or as POLYLINE if the line contains different heights          */
662/* -------------------------------------------------------------------- */
663    int bHasDifferentZ = FALSE;
664    if( poLS->getGeometryType() == wkbLineString25D )
665    {
666        double z0 = poLS->getZ(0);
667        for( int iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
668        {
669            if (z0 != poLS->getZ(iVert))
670            {
671                bHasDifferentZ = TRUE;
672                break;
673            }
674        }
675    }
676
677    WriteValue( 0, bHasDifferentZ ? "POLYLINE" : "LWPOLYLINE" );
678    WriteCore( poFeature );
679    WriteValue( 100, "AcDbEntity" );
680    if( bHasDifferentZ )
681    {
682        WriteValue( 100, "AcDb3dPolyline" );
683        WriteValue( 10, 0.0 );
684        WriteValue( 20, 0.0 );
685        WriteValue( 30, 0.0 );
686    }
687    else
688        WriteValue( 100, "AcDbPolyline" );
689    if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
690        WriteValue( 70, 1 + (bHasDifferentZ ? 8 : 0) );
691    else
692        WriteValue( 70, 0 + (bHasDifferentZ ? 8 : 0) );
693    if( !bHasDifferentZ )
694        WriteValue( 90, poLS->getNumPoints() );
695    else
696        WriteValue( 66, "1" );  // Vertex Flag
697
698/* -------------------------------------------------------------------- */
699/*      Do we have styling information?                                 */
700/* -------------------------------------------------------------------- */
701    OGRStyleTool *poTool = NULL;
702    OGRStyleMgr oSM;
703
704    if( poFeature->GetStyleString() != NULL )
705    {
706        oSM.InitFromFeature( poFeature );
707
708        if( oSM.GetPartCount() > 0 )
709            poTool = oSM.GetPart(0);
710    }
711
712/* -------------------------------------------------------------------- */
713/*      Handle a PEN tool to control drawing color and width.           */
714/*      Perhaps one day also dottedness, etc.                           */
715/* -------------------------------------------------------------------- */
716    if( poTool && poTool->GetType() == OGRSTCPen )
717    {
718        OGRStylePen *poPen = (OGRStylePen *) poTool;
719        GBool  bDefault;
720
721        if( poPen->Color(bDefault) != NULL && !bDefault )
722            WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) );
723
724        // we want to fetch the width in ground units.
725        poPen->SetUnit( OGRSTUGround, 1.0 );
726        double dfWidth = poPen->Width(bDefault);
727
728        if( !bDefault )
729            WriteValue( 370, (int) floor(dfWidth * 100 + 0.5) );
730    }
731
732/* -------------------------------------------------------------------- */
733/*      Do we have a Linetype for the feature?                          */
734/* -------------------------------------------------------------------- */
735    CPLString osLineType = poFeature->GetFieldAsString( "Linetype" );
736
737    if( osLineType.size() > 0
738        && (poDS->oHeaderDS.LookupLineType( osLineType ) != NULL
739            || oNewLineTypes.count(osLineType) > 0 ) )
740    {
741        // Already define -> just reference it.
742        WriteValue( 6, osLineType );
743    }
744    else if( poTool != NULL && poTool->GetType() == OGRSTCPen )
745    {
746        CPLString osDefinition = PrepareLineTypeDefinition( poFeature,
747                                                            poTool );
748
749        if( osDefinition != "" && osLineType == "" )
750        {
751            // Is this definition already created and named?
752            std::map<CPLString,CPLString>::iterator it;
753
754            for( it = oNewLineTypes.begin();
755                 it != oNewLineTypes.end();
756                 it++ )
757            {
758                if( (*it).second == osDefinition )
759                {
760                    osLineType = (*it).first;
761                    break;
762                }
763            }
764
765            // create an automatic name for it.
766            if( osLineType == "" )
767            {
768                do
769                {
770                    osLineType.Printf( "AutoLineType-%d", nNextAutoID++ );
771                }
772                while( poDS->oHeaderDS.LookupLineType(osLineType) != NULL );
773            }
774        }
775
776        // If it isn't already defined, add it now.
777        if( osDefinition != "" && oNewLineTypes.count(osLineType) == 0 )
778        {
779            oNewLineTypes[osLineType] = osDefinition;
780            WriteValue( 6, osLineType );
781        }
782    }
783
784/* -------------------------------------------------------------------- */
785/*      Write the vertices                                              */
786/* -------------------------------------------------------------------- */
787
788    if( !bHasDifferentZ && poLS->getGeometryType() == wkbLineString25D )
789    {
790     // if LWPOLYLINE with Z write it only once
791        if( !WriteValue( 38, poLS->getZ(0) ) )
792            return OGRERR_FAILURE;
793    }
794
795    int iVert;
796
797    for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
798    {
799        if( bHasDifferentZ )
800        {
801            WriteValue( 0, "VERTEX" );
802            WriteValue( 100, "AcDbEntity" );
803            WriteValue( 100, "AcDbVertex" );
804            WriteValue( 100, "AcDb3dPolylineVertex" );
805            WriteCore( poFeature );
806        }
807        WriteValue( 10, poLS->getX(iVert) );
808        if( !WriteValue( 20, poLS->getY(iVert) ) )
809            return OGRERR_FAILURE;
810
811        if( bHasDifferentZ )
812        {
813            if( !WriteValue( 30 , poLS->getZ(iVert) ) )
814                return OGRERR_FAILURE;
815            WriteValue( 70, 32 );
816        }
817    }
818
819    if( bHasDifferentZ )
820    {
821        WriteValue( 0, "SEQEND" );
822        WriteCore( poFeature );
823        WriteValue( 100, "AcDbEntity" );
824    }
825
826    delete poTool;
827
828    return OGRERR_NONE;
829
830#ifdef notdef
831/* -------------------------------------------------------------------- */
832/*      Alternate unmaintained implementation as a polyline entity.     */
833/* -------------------------------------------------------------------- */
834    WriteValue( 0, "POLYLINE" );
835    WriteCore( poFeature );
836    WriteValue( 100, "AcDbEntity" );
837    WriteValue( 100, "AcDbPolyline" );
838    if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
839        WriteValue( 70, 1 );
840    else
841        WriteValue( 70, 0 );
842    WriteValue( 66, "1" );
843
844    int iVert;
845
846    for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
847    {
848        WriteValue( 0, "VERTEX" );
849        WriteValue( 8, "0" );
850        WriteValue( 10, poLS->getX(iVert) );
851        if( !WriteValue( 20, poLS->getY(iVert) ) )
852            return OGRERR_FAILURE;
853
854        if( poLS->getGeometryType() == wkbLineString25D )
855        {
856            if( !WriteValue( 30, poLS->getZ(iVert) ) )
857                return OGRERR_FAILURE;
858        }
859    }
860
861    WriteValue( 0, "SEQEND" );
862    WriteValue( 8, "0" );
863
864    return OGRERR_NONE;
865#endif
866}
867
868/************************************************************************/
869/*                             WriteHATCH()                             */
870/************************************************************************/
871
872OGRErr OGRDXFWriterLayer::WriteHATCH( OGRFeature *poFeature,
873                                      OGRGeometry *poGeom )
874
875{
876/* -------------------------------------------------------------------- */
877/*      For now we handle multipolygons by writing a series of          */
878/*      entities.                                                       */
879/* -------------------------------------------------------------------- */
880    if( poGeom == NULL )
881        poGeom = poFeature->GetGeometryRef();
882
883    if ( poGeom->IsEmpty() )
884    {
885        return OGRERR_NONE;
886    }
887
888    if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon )
889    {
890        OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom;
891        int iGeom;
892        OGRErr eErr = OGRERR_NONE;
893
894        for( iGeom = 0;
895             eErr == OGRERR_NONE && iGeom < poGC->getNumGeometries();
896             iGeom++ )
897        {
898            eErr = WriteHATCH( poFeature, poGC->getGeometryRef( iGeom ) );
899        }
900
901        return eErr;
902    }
903
904/* -------------------------------------------------------------------- */
905/*      Do we now have a geometry we can work with?                     */
906/* -------------------------------------------------------------------- */
907    if( wkbFlatten(poGeom->getGeometryType()) != wkbPolygon )
908        return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
909
910/* -------------------------------------------------------------------- */
911/*      Write as a hatch.                                               */
912/* -------------------------------------------------------------------- */
913    WriteValue( 0, "HATCH" );
914    WriteCore( poFeature );
915    WriteValue( 100, "AcDbEntity" );
916    WriteValue( 100, "AcDbHatch" );
917
918    WriteValue( 10, 0 ); // elevation point X. 0 for DXF
919    WriteValue( 20, 0 ); // elevation point Y
920    WriteValue( 30, 0 ); // elevation point Z
921    WriteValue(210, 0 ); // extrusion direction X
922    WriteValue(220, 0 ); // extrusion direction Y
923    WriteValue(230,1.0); // extrusion direction Z
924
925    WriteValue( 2, "SOLID" ); // fill pattern
926    WriteValue( 70, 1 ); // solid fill
927    WriteValue( 71, 0 ); // associativity
928
929/* -------------------------------------------------------------------- */
930/*      Do we have styling information?                                 */
931/* -------------------------------------------------------------------- */
932    OGRStyleTool *poTool = NULL;
933    OGRStyleMgr oSM;
934
935    if( poFeature->GetStyleString() != NULL )
936    {
937        oSM.InitFromFeature( poFeature );
938
939        if( oSM.GetPartCount() > 0 )
940            poTool = oSM.GetPart(0);
941    }
942    // Write style brush fore color
943    if( poTool && poTool->GetType() == OGRSTCBrush )
944    {
945        OGRStyleBrush *poBrush = (OGRStyleBrush *) poTool;
946        GBool  bDefault;
947
948        if( poBrush->ForeColor(bDefault) != NULL && !bDefault )
949            WriteValue( 62, ColorStringToDXFColor( poBrush->ForeColor(bDefault) ) );
950    }
951    delete poTool;
952
953/* -------------------------------------------------------------------- */
954/*      Handle a PEN tool to control drawing color and width.           */
955/*      Perhaps one day also dottedness, etc.                           */
956/* -------------------------------------------------------------------- */
957#ifdef notdef
958    if( poTool && poTool->GetType() == OGRSTCPen )
959    {
960        OGRStylePen *poPen = (OGRStylePen *) poTool;
961        GBool  bDefault;
962
963        if( poPen->Color(bDefault) != NULL && !bDefault )
964            WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) );
965
966        double dfWidthInMM = poPen->Width(bDefault);
967
968        if( !bDefault )
969            WriteValue( 370, (int) floor(dfWidthInMM * 100 + 0.5) );
970    }
971
972/* -------------------------------------------------------------------- */
973/*      Do we have a Linetype for the feature?                          */
974/* -------------------------------------------------------------------- */
975    CPLString osLineType = poFeature->GetFieldAsString( "Linetype" );
976
977    if( osLineType.size() > 0
978        && (poDS->oHeaderDS.LookupLineType( osLineType ) != NULL
979            || oNewLineTypes.count(osLineType) > 0 ) )
980    {
981        // Already define -> just reference it.
982        WriteValue( 6, osLineType );
983    }
984    else if( poTool != NULL && poTool->GetType() == OGRSTCPen )
985    {
986        CPLString osDefinition = PrepareLineTypeDefinition( poFeature,
987                                                            poTool );
988
989        if( osDefinition != "" && osLineType == "" )
990        {
991            // Is this definition already created and named?
992            std::map<CPLString,CPLString>::iterator it;
993
994            for( it = oNewLineTypes.begin();
995                 it != oNewLineTypes.end();
996                 it++ )
997            {
998                if( (*it).second == osDefinition )
999                {
1000                    osLineType = (*it).first;
1001                    break;
1002                }
1003            }
1004
1005            // create an automatic name for it.
1006            if( osLineType == "" )
1007            {
1008                do
1009                {
1010                    osLineType.Printf( "AutoLineType-%d", nNextAutoID++ );
1011                }
1012                while( poDS->oHeaderDS.LookupLineType(osLineType) != NULL );
1013            }
1014        }
1015
1016        // If it isn't already defined, add it now.
1017        if( osDefinition != "" && oNewLineTypes.count(osLineType) == 0 )
1018        {
1019            oNewLineTypes[osLineType] = osDefinition;
1020            WriteValue( 6, osLineType );
1021        }
1022    }
1023    delete poTool;
1024#endif
1025
1026/* -------------------------------------------------------------------- */
1027/*      Process the loops (rings).                                      */
1028/* -------------------------------------------------------------------- */
1029    OGRPolygon *poPoly = (OGRPolygon *) poGeom;
1030
1031    WriteValue( 91, poPoly->getNumInteriorRings() + 1 );
1032
1033    for( int iRing = -1; iRing < poPoly->getNumInteriorRings(); iRing++ )
1034    {
1035        OGRLinearRing *poLR;
1036
1037        if( iRing == -1 )
1038            poLR = poPoly->getExteriorRing();
1039        else
1040            poLR = poPoly->getInteriorRing( iRing );
1041
1042        WriteValue( 92, 2 ); // Polyline
1043        WriteValue( 72, 0 ); // has bulge
1044        WriteValue( 73, 1 ); // is closed
1045        WriteValue( 93, poLR->getNumPoints() );
1046
1047        for( int iVert = 0; iVert < poLR->getNumPoints(); iVert++ )
1048        {
1049            WriteValue( 10, poLR->getX(iVert) );
1050            WriteValue( 20, poLR->getY(iVert) );
1051        }
1052
1053        WriteValue( 97, 0 ); // 0 source boundary objects
1054    }
1055
1056    WriteValue( 75, 0 ); // hatch style = Hatch "odd parity" area (Normal style)
1057    WriteValue( 76, 1 ); // hatch pattern type = predefined
1058    WriteValue( 98, 0 ); // 0 seed points
1059
1060    return OGRERR_NONE;
1061
1062#ifdef notdef
1063/* -------------------------------------------------------------------- */
1064/*      Alternate unmaintained implementation as a polyline entity.     */
1065/* -------------------------------------------------------------------- */
1066    WriteValue( 0, "POLYLINE" );
1067    WriteCore( poFeature );
1068    WriteValue( 100, "AcDbEntity" );
1069    WriteValue( 100, "AcDbPolyline" );
1070    if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
1071        WriteValue( 70, 1 );
1072    else
1073        WriteValue( 70, 0 );
1074    WriteValue( 66, "1" );
1075
1076    int iVert;
1077
1078    for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
1079    {
1080        WriteValue( 0, "VERTEX" );
1081        WriteValue( 8, "0" );
1082        WriteValue( 10, poLS->getX(iVert) );
1083        if( !WriteValue( 20, poLS->getY(iVert) ) )
1084            return OGRERR_FAILURE;
1085
1086        if( poLS->getGeometryType() == wkbLineString25D )
1087        {
1088            if( !WriteValue( 30, poLS->getZ(iVert) ) )
1089                return OGRERR_FAILURE;
1090        }
1091    }
1092
1093    WriteValue( 0, "SEQEND" );
1094    WriteValue( 8, "0" );
1095
1096    return OGRERR_NONE;
1097#endif
1098}
1099
1100/************************************************************************/
1101/*                           ICreateFeature()                            */
1102/************************************************************************/
1103
1104OGRErr OGRDXFWriterLayer::ICreateFeature( OGRFeature *poFeature )
1105
1106{
1107    OGRGeometry *poGeom = poFeature->GetGeometryRef();
1108    OGRwkbGeometryType eGType = wkbNone;
1109
1110    if( poGeom != NULL )
1111    {
1112        if( !poGeom->IsEmpty() )
1113        {
1114            OGREnvelope sEnvelope;
1115            poGeom->getEnvelope(&sEnvelope);
1116            poDS->UpdateExtent(&sEnvelope);
1117        }
1118        eGType = wkbFlatten(poGeom->getGeometryType());
1119    }
1120
1121    if( eGType == wkbPoint )
1122    {
1123        const char *pszBlockName = poFeature->GetFieldAsString("BlockName");
1124
1125        // we don't want to treat as a block ref if we are writing blocks layer
1126        if( pszBlockName != NULL
1127            && poDS->poBlocksLayer != NULL
1128            && poFeature->GetDefnRef() == poDS->poBlocksLayer->GetLayerDefn())
1129            pszBlockName = NULL;
1130
1131        // We don't want to treat as a blocks ref if the block is not defined
1132        if( pszBlockName
1133            && poDS->oHeaderDS.LookupBlock(pszBlockName) == NULL )
1134        {
1135            if( poDS->poBlocksLayer == NULL
1136                || poDS->poBlocksLayer->FindBlock(pszBlockName) == NULL )
1137                pszBlockName = NULL;
1138        }
1139
1140        if( pszBlockName != NULL )
1141            return WriteINSERT( poFeature );
1142
1143        else if( poFeature->GetStyleString() != NULL
1144            && STARTS_WITH_CI(poFeature->GetStyleString(), "LABEL") )
1145            return WriteTEXT( poFeature );
1146        else
1147            return WritePOINT( poFeature );
1148    }
1149    else if( eGType == wkbLineString
1150             || eGType == wkbMultiLineString )
1151        return WritePOLYLINE( poFeature );
1152
1153    else if( eGType == wkbPolygon
1154             || eGType == wkbMultiPolygon )
1155    {
1156        if( bWriteHatch )
1157            return WriteHATCH( poFeature );
1158        else
1159            return WritePOLYLINE( poFeature );
1160    }
1161
1162    // Explode geometry collections into multiple entities.
1163    else if( eGType == wkbGeometryCollection )
1164    {
1165        OGRGeometryCollection *poGC = (OGRGeometryCollection *)
1166            poFeature->StealGeometry();
1167        int iGeom;
1168
1169        for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ )
1170        {
1171            poFeature->SetGeometry( poGC->getGeometryRef(iGeom) );
1172
1173            OGRErr eErr = CreateFeature( poFeature );
1174
1175            if( eErr != OGRERR_NONE )
1176                return eErr;
1177
1178        }
1179
1180        poFeature->SetGeometryDirectly( poGC );
1181        return OGRERR_NONE;
1182    }
1183    else
1184    {
1185        CPLError( CE_Failure, CPLE_AppDefined,
1186                  "No known way to write feature with geometry '%s'.",
1187                  OGRGeometryTypeToName(eGType) );
1188        return OGRERR_FAILURE;
1189    }
1190}
1191
1192/************************************************************************/
1193/*                       ColorStringToDXFColor()                        */
1194/************************************************************************/
1195
1196int OGRDXFWriterLayer::ColorStringToDXFColor( const char *pszRGB )
1197
1198{
1199/* -------------------------------------------------------------------- */
1200/*      Parse the RGB string.                                           */
1201/* -------------------------------------------------------------------- */
1202    if( pszRGB == NULL )
1203        return -1;
1204
1205    int nRed, nGreen, nBlue, nTransparency = 255;
1206
1207    int nCount  = sscanf(pszRGB,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,
1208                         &nTransparency);
1209
1210    if (nCount < 3 )
1211        return -1;
1212
1213/* -------------------------------------------------------------------- */
1214/*      Find near color in DXF palette.                                 */
1215/* -------------------------------------------------------------------- */
1216    const unsigned char *pabyDXFColors = ACGetColorTable();
1217    int i;
1218    int nMinDist = 768;
1219    int nBestColor = -1;
1220
1221    for( i = 1; i < 256; i++ )
1222    {
1223        int nDist = ABS(nRed - pabyDXFColors[i*3+0])
1224            + ABS(nGreen - pabyDXFColors[i*3+1])
1225            + ABS(nBlue  - pabyDXFColors[i*3+2]);
1226
1227        if( nDist < nMinDist )
1228        {
1229            nBestColor = i;
1230            nMinDist = nDist;
1231        }
1232    }
1233
1234    return nBestColor;
1235}
Note: See TracBrowser for help on using the repository browser.