1 /******************************************************************************
2  * $Id: ogrdgnlayer.cpp 28382 2015-01-30 15:29:41Z rouault $
3  *
4  * Project:  OpenGIS Simple Features Reference Implementation
5  * Purpose:  Implements OGRDGNLayer class.
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  ******************************************************************************
9  * Copyright (c) 2000, Frank Warmerdam (warmerdam@pobox.com)
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "ogr_dgn.h"
31 #include "cpl_conv.h"
32 #include "ogr_featurestyle.h"
33 #include "ogr_api.h"
34 #include <list>
35 
36 CPL_CVSID("$Id: ogrdgnlayer.cpp 28382 2015-01-30 15:29:41Z rouault $");
37 
38 /************************************************************************/
39 /*                           OGRDGNLayer()                              */
40 /************************************************************************/
41 
OGRDGNLayer(const char * pszName,DGNHandle hDGN,int bUpdate)42 OGRDGNLayer::OGRDGNLayer( const char * pszName, DGNHandle hDGN,
43                           int bUpdate )
44 
45 {
46     this->hDGN = hDGN;
47     this->bUpdate = bUpdate;
48 
49 /* -------------------------------------------------------------------- */
50 /*      Work out what link format we are using.                         */
51 /* -------------------------------------------------------------------- */
52     OGRFieldType eLinkFieldType;
53 
54     pszLinkFormat = (char *) CPLGetConfigOption( "DGN_LINK_FORMAT", "FIRST" );
55     if( EQUAL(pszLinkFormat,"FIRST") )
56         eLinkFieldType = OFTInteger;
57     else if( EQUAL(pszLinkFormat,"LIST") )
58         eLinkFieldType = OFTIntegerList;
59     else if( EQUAL(pszLinkFormat,"STRING") )
60         eLinkFieldType = OFTString;
61     else
62     {
63         CPLError( CE_Warning, CPLE_AppDefined,
64                   "DGN_LINK_FORMAT=%s, but only FIRST, LIST or STRING supported.",
65                   pszLinkFormat );
66         pszLinkFormat = (char *) "FIRST";
67         eLinkFieldType = OFTInteger;
68     }
69     pszLinkFormat = CPLStrdup(pszLinkFormat);
70 
71 /* -------------------------------------------------------------------- */
72 /*      Create the feature definition.                                  */
73 /* -------------------------------------------------------------------- */
74     poFeatureDefn = new OGRFeatureDefn( pszName );
75     SetDescription( poFeatureDefn->GetName() );
76     poFeatureDefn->Reference();
77 
78     OGRFieldDefn        oField( "", OFTInteger );
79 
80 /* -------------------------------------------------------------------- */
81 /*      Element type                                                    */
82 /* -------------------------------------------------------------------- */
83     oField.SetName( "Type" );
84     oField.SetType( OFTInteger );
85     oField.SetWidth( 2 );
86     oField.SetPrecision( 0 );
87     poFeatureDefn->AddFieldDefn( &oField );
88 
89 /* -------------------------------------------------------------------- */
90 /*      Level number.                                                   */
91 /* -------------------------------------------------------------------- */
92     oField.SetName( "Level" );
93     oField.SetType( OFTInteger );
94     oField.SetWidth( 2 );
95     oField.SetPrecision( 0 );
96     poFeatureDefn->AddFieldDefn( &oField );
97 
98 /* -------------------------------------------------------------------- */
99 /*      graphic group                                                   */
100 /* -------------------------------------------------------------------- */
101     oField.SetName( "GraphicGroup" );
102     oField.SetType( OFTInteger );
103     oField.SetWidth( 4 );
104     oField.SetPrecision( 0 );
105     poFeatureDefn->AddFieldDefn( &oField );
106 
107 /* -------------------------------------------------------------------- */
108 /*      ColorIndex                                                      */
109 /* -------------------------------------------------------------------- */
110     oField.SetName( "ColorIndex" );
111     oField.SetType( OFTInteger );
112     oField.SetWidth( 3 );
113     oField.SetPrecision( 0 );
114     poFeatureDefn->AddFieldDefn( &oField );
115 
116 /* -------------------------------------------------------------------- */
117 /*      Weight                                                          */
118 /* -------------------------------------------------------------------- */
119     oField.SetName( "Weight" );
120     oField.SetType( OFTInteger );
121     oField.SetWidth( 2 );
122     oField.SetPrecision( 0 );
123     poFeatureDefn->AddFieldDefn( &oField );
124 
125 /* -------------------------------------------------------------------- */
126 /*      Style                                                           */
127 /* -------------------------------------------------------------------- */
128     oField.SetName( "Style" );
129     oField.SetType( OFTInteger );
130     oField.SetWidth( 1 );
131     oField.SetPrecision( 0 );
132     poFeatureDefn->AddFieldDefn( &oField );
133 
134 /* -------------------------------------------------------------------- */
135 /*      EntityNum                                                       */
136 /* -------------------------------------------------------------------- */
137     oField.SetName( "EntityNum" );
138     oField.SetType( eLinkFieldType );
139     oField.SetWidth( 0 );
140     oField.SetPrecision( 0 );
141     poFeatureDefn->AddFieldDefn( &oField );
142 
143 /* -------------------------------------------------------------------- */
144 /*      MSLink                                                          */
145 /* -------------------------------------------------------------------- */
146     oField.SetName( "MSLink" );
147     oField.SetType( eLinkFieldType );
148     oField.SetWidth( 0 );
149     oField.SetPrecision( 0 );
150     poFeatureDefn->AddFieldDefn( &oField );
151 
152 /* -------------------------------------------------------------------- */
153 /*      Text                                                            */
154 /* -------------------------------------------------------------------- */
155     oField.SetName( "Text" );
156     oField.SetType( OFTString );
157     oField.SetWidth( 0 );
158     oField.SetPrecision( 0 );
159     poFeatureDefn->AddFieldDefn( &oField );
160 
161 /* -------------------------------------------------------------------- */
162 /*      Create template feature for evaluating simple expressions.      */
163 /* -------------------------------------------------------------------- */
164     bHaveSimpleQuery = FALSE;
165     poEvalFeature = new OGRFeature( poFeatureDefn );
166 
167     /* TODO: I am intending to keep track of simple attribute queries (ones
168        using only FID, Type and Level and short circuiting their operation
169        based on the index.  However, there are some complexities with
170        complex elements, and spatial queries that have caused me to put it
171        off for now.
172     */
173 }
174 
175 /************************************************************************/
176 /*                           ~OGRDGNLayer()                             */
177 /************************************************************************/
178 
~OGRDGNLayer()179 OGRDGNLayer::~OGRDGNLayer()
180 
181 {
182     if( m_nFeaturesRead > 0 && poFeatureDefn != NULL )
183     {
184         CPLDebug( "Mem", "%d features read on layer '%s'.",
185                   (int) m_nFeaturesRead,
186                   poFeatureDefn->GetName() );
187     }
188 
189     delete poEvalFeature;
190 
191     poFeatureDefn->Release();
192 
193     CPLFree( pszLinkFormat );
194 }
195 
196 /************************************************************************/
197 /*                          SetSpatialFilter()                          */
198 /************************************************************************/
199 
SetSpatialFilter(OGRGeometry * poGeomIn)200 void OGRDGNLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
201 
202 {
203     if( !InstallFilter(poGeomIn) )
204         return;
205 
206     if( m_poFilterGeom != NULL )
207     {
208         DGNSetSpatialFilter( hDGN,
209                              m_sFilterEnvelope.MinX,
210                              m_sFilterEnvelope.MinY,
211                              m_sFilterEnvelope.MaxX,
212                              m_sFilterEnvelope.MaxY );
213     }
214     else
215     {
216         DGNSetSpatialFilter( hDGN, 0.0, 0.0, 0.0, 0.0 );
217     }
218 
219     ResetReading();
220 }
221 
222 /************************************************************************/
223 /*                            ResetReading()                            */
224 /************************************************************************/
225 
ResetReading()226 void OGRDGNLayer::ResetReading()
227 
228 {
229     iNextShapeId = 0;
230     DGNRewind( hDGN );
231 }
232 
233 /************************************************************************/
234 /*                             GetFeature()                             */
235 /************************************************************************/
236 
GetFeature(GIntBig nFeatureId)237 OGRFeature *OGRDGNLayer::GetFeature( GIntBig nFeatureId )
238 
239 {
240     OGRFeature *poFeature;
241     DGNElemCore *psElement;
242 
243     if( nFeatureId > INT_MAX || !DGNGotoElement( hDGN, (int)nFeatureId ) )
244         return NULL;
245 
246     // We should likely clear the spatial search region as it affects
247     // DGNReadElement() but I will defer that for now.
248 
249     psElement = DGNReadElement( hDGN );
250     poFeature = ElementToFeature( psElement );
251     DGNFreeElement( hDGN, psElement );
252 
253     if( poFeature == NULL )
254         return NULL;
255 
256     if( poFeature->GetFID() != nFeatureId )
257     {
258         delete poFeature;
259         return NULL;
260     }
261 
262     return poFeature;
263 }
264 
265 /************************************************************************/
266 /*                           ConsiderBrush()                            */
267 /*                                                                      */
268 /*      Method to set the style for a polygon, including a brush if     */
269 /*      appropriate.                                                    */
270 /************************************************************************/
271 
ConsiderBrush(DGNElemCore * psElement,const char * pszPen,OGRFeature * poFeature)272 void OGRDGNLayer::ConsiderBrush( DGNElemCore *psElement, const char *pszPen,
273                                  OGRFeature *poFeature )
274 
275 {
276     int         gv_red, gv_green, gv_blue;
277     char                szFullStyle[256];
278     int                 nFillColor;
279 
280     if( DGNGetShapeFillInfo( hDGN, psElement, &nFillColor )
281         && DGNLookupColor( hDGN, nFillColor,
282                            &gv_red, &gv_green, &gv_blue ) )
283     {
284         sprintf( szFullStyle,
285                  "BRUSH(fc:#%02x%02x%02x,id:\"ogr-brush-0\")",
286                  gv_red, gv_green, gv_blue );
287 
288         if( nFillColor != psElement->color )
289         {
290             strcat( szFullStyle, ";" );
291             strcat( szFullStyle, pszPen );
292         }
293         poFeature->SetStyleString( szFullStyle );
294     }
295     else
296         poFeature->SetStyleString( pszPen );
297 }
298 
299 /************************************************************************/
300 /*                          ElementToFeature()                          */
301 /************************************************************************/
302 
ElementToFeature(DGNElemCore * psElement)303 OGRFeature *OGRDGNLayer::ElementToFeature( DGNElemCore *psElement )
304 
305 {
306     OGRFeature  *poFeature = new OGRFeature( poFeatureDefn );
307 
308     poFeature->SetFID( psElement->element_id );
309     poFeature->SetField( "Type", psElement->type );
310     poFeature->SetField( "Level", psElement->level );
311     poFeature->SetField( "GraphicGroup", psElement->graphic_group );
312     poFeature->SetField( "ColorIndex", psElement->color );
313     poFeature->SetField( "Weight", psElement->weight );
314     poFeature->SetField( "Style", psElement->style );
315 
316 
317     m_nFeaturesRead++;
318 
319 /* -------------------------------------------------------------------- */
320 /*      Collect linkage information                                     */
321 /* -------------------------------------------------------------------- */
322 #define MAX_LINK 100
323     int anEntityNum[MAX_LINK], anMSLink[MAX_LINK];
324     unsigned char *pabyData;
325     int iLink=0, nLinkCount=0;
326 
327     anEntityNum[0] = 0;
328     anMSLink[0] = 0;
329 
330     pabyData = DGNGetLinkage( hDGN, psElement, iLink, NULL,
331                               anEntityNum+iLink, anMSLink+iLink, NULL );
332     while( pabyData && nLinkCount < MAX_LINK )
333     {
334         iLink++;
335 
336         if( anEntityNum[nLinkCount] != 0 || anMSLink[nLinkCount] != 0 )
337             nLinkCount++;
338 
339         anEntityNum[nLinkCount] = 0;
340         anMSLink[nLinkCount] = 0;
341 
342         pabyData = DGNGetLinkage( hDGN, psElement, iLink, NULL,
343                                   anEntityNum+nLinkCount, anMSLink+nLinkCount,
344                                   NULL );
345     }
346 
347 /* -------------------------------------------------------------------- */
348 /*      Apply attribute linkage to feature.                             */
349 /* -------------------------------------------------------------------- */
350     if( nLinkCount > 0 )
351     {
352         if( EQUAL(pszLinkFormat,"FIRST") )
353         {
354             poFeature->SetField( "EntityNum", anEntityNum[0] );
355             poFeature->SetField( "MSLink", anMSLink[0] );
356         }
357         else if( EQUAL(pszLinkFormat,"LIST") )
358         {
359             poFeature->SetField( "EntityNum", nLinkCount, anEntityNum );
360             poFeature->SetField( "MSLink", nLinkCount, anMSLink );
361         }
362         else if( EQUAL(pszLinkFormat,"STRING") )
363         {
364             char szEntityList[MAX_LINK*9], szMSLinkList[MAX_LINK*9];
365             int nEntityLen = 0, nMSLinkLen = 0;
366 
367             for( iLink = 0; iLink < nLinkCount; iLink++ )
368             {
369                 if( iLink != 0 )
370                 {
371                     szEntityList[nEntityLen++] = ',';
372                     szMSLinkList[nMSLinkLen++] = ',';
373                 }
374 
375                 sprintf( szEntityList + nEntityLen, "%d", anEntityNum[iLink]);
376                 sprintf( szMSLinkList + nMSLinkLen, "%d", anMSLink[iLink] );
377 
378                 nEntityLen += strlen(szEntityList + nEntityLen );
379                 nMSLinkLen += strlen(szMSLinkList + nMSLinkLen );
380             }
381 
382             poFeature->SetField( "EntityNum", szEntityList );
383             poFeature->SetField( "MSLink", szMSLinkList );
384         }
385     }
386 
387 /* -------------------------------------------------------------------- */
388 /*      Lookup color.                                                   */
389 /* -------------------------------------------------------------------- */
390     char        gv_color[128];
391     int         gv_red, gv_green, gv_blue;
392     char        szFSColor[128], szPen[256];
393 
394     szFSColor[0] = '\0';
395     if( DGNLookupColor( hDGN, psElement->color,
396                         &gv_red, &gv_green, &gv_blue ) )
397     {
398         sprintf( gv_color, "%f %f %f 1.0",
399                  gv_red / 255.0, gv_green / 255.0, gv_blue / 255.0 );
400 
401         sprintf( szFSColor, "c:#%02x%02x%02x",
402                  gv_red, gv_green, gv_blue );
403     }
404 
405 /* -------------------------------------------------------------------- */
406 /*      Generate corresponding PEN style.                               */
407 /* -------------------------------------------------------------------- */
408     if( psElement->style == DGNS_SOLID )
409         sprintf( szPen, "PEN(id:\"ogr-pen-0\"" );
410     else if( psElement->style == DGNS_DOTTED )
411         sprintf( szPen, "PEN(id:\"ogr-pen-5\"" );
412     else if( psElement->style == DGNS_MEDIUM_DASH )
413         sprintf( szPen, "PEN(id:\"ogr-pen-2\"" );
414     else if( psElement->style == DGNS_LONG_DASH )
415         sprintf( szPen, "PEN(id:\"ogr-pen-4\"" );
416     else if( psElement->style == DGNS_DOT_DASH )
417         sprintf( szPen, "PEN(id:\"ogr-pen-6\"" );
418     else if( psElement->style == DGNS_SHORT_DASH )
419         sprintf( szPen, "PEN(id:\"ogr-pen-3\"" );
420     else if( psElement->style == DGNS_DASH_DOUBLE_DOT )
421         sprintf( szPen, "PEN(id:\"ogr-pen-7\"" );
422     else if( psElement->style == DGNS_LONG_DASH_SHORT_DASH )
423         sprintf( szPen, "PEN(p:\"10px 5px 4px 5px\"" );
424     else
425         sprintf( szPen, "PEN(id:\"ogr-pen-0\"" );
426 
427     if( strlen(szFSColor) > 0 )
428         sprintf( szPen+strlen(szPen), ",%s", szFSColor );
429 
430     if( psElement->weight > 1 )
431         sprintf( szPen+strlen(szPen), ",w:%dpx", psElement->weight );
432 
433     strcat( szPen, ")" );
434 
435     switch( psElement->stype )
436     {
437       case DGNST_MULTIPOINT:
438         if( psElement->type == DGNT_SHAPE )
439         {
440             OGRLinearRing       *poLine = new OGRLinearRing();
441             OGRPolygon          *poPolygon = new OGRPolygon();
442             DGNElemMultiPoint *psEMP = (DGNElemMultiPoint *) psElement;
443 
444             poLine->setNumPoints( psEMP->num_vertices );
445             for( int i = 0; i < psEMP->num_vertices; i++ )
446             {
447                 poLine->setPoint( i,
448                                   psEMP->vertices[i].x,
449                                   psEMP->vertices[i].y,
450                                   psEMP->vertices[i].z );
451             }
452 
453             poPolygon->addRingDirectly( poLine );
454 
455             poFeature->SetGeometryDirectly( poPolygon );
456 
457             ConsiderBrush( psElement, szPen, poFeature );
458         }
459         else if( psElement->type == DGNT_CURVE )
460         {
461             DGNElemMultiPoint *psEMP = (DGNElemMultiPoint *) psElement;
462             OGRLineString       *poLine = new OGRLineString();
463             DGNPoint            *pasPoints;
464             int                 nPoints;
465 
466             nPoints = 5 * psEMP->num_vertices;
467             pasPoints = (DGNPoint *) CPLMalloc(sizeof(DGNPoint) * nPoints);
468 
469             DGNStrokeCurve( hDGN, psEMP, nPoints, pasPoints );
470 
471             poLine->setNumPoints( nPoints );
472             for( int i = 0; i < nPoints; i++ )
473             {
474                 poLine->setPoint( i,
475                                   pasPoints[i].x,
476                                   pasPoints[i].y,
477                                   pasPoints[i].z );
478             }
479 
480             poFeature->SetGeometryDirectly( poLine );
481             CPLFree( pasPoints );
482 
483             poFeature->SetStyleString( szPen );
484         }
485         else
486         {
487             OGRLineString       *poLine = new OGRLineString();
488             DGNElemMultiPoint *psEMP = (DGNElemMultiPoint *) psElement;
489 
490             if( psEMP->num_vertices > 0 )
491             {
492                 poLine->setNumPoints( psEMP->num_vertices );
493                 for( int i = 0; i < psEMP->num_vertices; i++ )
494                 {
495                     poLine->setPoint( i,
496                                       psEMP->vertices[i].x,
497                                       psEMP->vertices[i].y,
498                                       psEMP->vertices[i].z );
499                 }
500 
501                 poFeature->SetGeometryDirectly( poLine );
502             }
503 
504             poFeature->SetStyleString( szPen );
505         }
506         break;
507 
508       case DGNST_ARC:
509       {
510           OGRLineString *poLine = new OGRLineString();
511           DGNElemArc    *psArc = (DGNElemArc *) psElement;
512           DGNPoint      asPoints[90];
513           int           nPoints;
514 
515           nPoints = (int) (MAX(1,ABS(psArc->sweepang) / 5) + 1);
516           DGNStrokeArc( hDGN, psArc, nPoints, asPoints );
517 
518           poLine->setNumPoints( nPoints );
519           for( int i = 0; i < nPoints; i++ )
520           {
521               poLine->setPoint( i,
522                                 asPoints[i].x,
523                                 asPoints[i].y,
524                                 asPoints[i].z );
525           }
526 
527           poFeature->SetGeometryDirectly( poLine );
528           poFeature->SetStyleString( szPen );
529       }
530       break;
531 
532       case DGNST_TEXT:
533       {
534           OGRPoint      *poPoint = new OGRPoint();
535           DGNElemText   *psText = (DGNElemText *) psElement;
536           char          *pszOgrFS;
537 
538           poPoint->setX( psText->origin.x );
539           poPoint->setY( psText->origin.y );
540           poPoint->setZ( psText->origin.z );
541 
542           poFeature->SetGeometryDirectly( poPoint );
543 
544           pszOgrFS = (char *) CPLMalloc(strlen(psText->string) + 150);
545 
546           // setup the basic label.
547           sprintf( pszOgrFS, "LABEL(t:\"%s\"",  psText->string );
548 
549           // set the color if we have it.
550           if( strlen(szFSColor) > 0 )
551               sprintf( pszOgrFS+strlen(pszOgrFS), ",%s", szFSColor );
552 
553           // Add the size info in ground units.
554           if( ABS(psText->height_mult) >= 6.0 )
555               CPLsprintf( pszOgrFS+strlen(pszOgrFS), ",s:%dg",
556                        (int) psText->height_mult );
557           else if( ABS(psText->height_mult) > 0.1 )
558               CPLsprintf( pszOgrFS+strlen(pszOgrFS), ",s:%.3fg",
559                        psText->height_mult );
560           else
561               CPLsprintf( pszOgrFS+strlen(pszOgrFS), ",s:%.12fg",
562                        psText->height_mult );
563 
564           // Add the font name. Name it MstnFont<FONTNUMBER> if not available
565           // in the font list. #3392
566           static const char *papszFontList[] =
567           { "STANDARD", "WORKING", "FANCY", "ENGINEERING", "NEWZERO", "STENCEL", //0-5
568             "USTN_FANCY", "COMPRESSED", "STENCEQ", NULL, "hand", "ARCH", //6-11
569             "ARCHB", NULL, NULL, "IGES1001", "IGES1002", "IGES1003", //12-17
570             "CENTB", "MICROS", NULL, NULL, "ISOFRACTIONS", "ITALICS", //18-23
571             "ISO30", NULL, "GREEK", "ISOREC", "Isoeq", NULL, //24-29
572             "ISO_FONTLEFT", "ISO_FONTRIGHT", "INTL_ENGINEERING", "INTL_WORKING", "ISOITEQ", NULL, //30-35
573             "USTN FONT 26", NULL, NULL, NULL, NULL, "ARCHITECTURAL", //36-41
574             "BLOCK_OUTLINE", "LOW_RES_FILLED", NULL, NULL, NULL, NULL, //42-47
575             NULL, NULL, "UPPERCASE", NULL, NULL, NULL, //48-53
576             NULL, NULL, NULL, NULL, NULL, NULL, //54-49
577             "FONT060", "din", "dinit", "helvl", "HELVLIT", "helv", //60-65
578             "HELVIT", "cent", "CENTIT", "SCRIPT", NULL, NULL, //66-71
579             NULL, NULL, NULL, NULL, "MICROQ", "dotfont", //72-77
580             "DOTIT", NULL, NULL, NULL, NULL, NULL, //78-83
581             NULL, NULL, NULL, NULL, NULL, NULL, //84-89
582             NULL, NULL, "FONT092", NULL, "FONT094", NULL, //90-95
583             NULL, NULL, NULL, NULL, "ANSI_SYMBOLS", "FEATURE_CONTROL_SYSMBOLS", //96-101
584             "SYMB_FAST", NULL, NULL, "INTL_ISO", "INTL_ISO_EQUAL", "INTL_ISO_ITALIC", //102-107
585             "INTL_ISO_ITALIC_EQUAL" }; //108
586 
587           if(psText->font_id <= 108 && papszFontList[psText->font_id] != NULL )
588           {
589               sprintf( pszOgrFS+strlen(pszOgrFS), ",f:%s",
590                        papszFontList[psText->font_id] );
591           }
592           else
593           {
594               sprintf( pszOgrFS+strlen(pszOgrFS), ",f:MstnFont%d",
595                        psText->font_id );
596           }
597 
598           // Add the angle, if not horizontal
599           if( psText->rotation != 0.0 )
600               sprintf( pszOgrFS+strlen(pszOgrFS), ",a:%d",
601                        (int) (psText->rotation+0.5) );
602 
603           strcat( pszOgrFS, ")" );
604 
605           poFeature->SetStyleString( pszOgrFS );
606           CPLFree( pszOgrFS );
607 
608           poFeature->SetField( "Text", psText->string );
609       }
610       break;
611 
612       case DGNST_COMPLEX_HEADER:
613       {
614           DGNElemComplexHeader *psHdr = (DGNElemComplexHeader *) psElement;
615           int           iChild;
616           OGRMultiLineString  oChildren;
617 
618           /* collect subsequent child geometries. */
619           // we should disable the spatial filter ... add later.
620           for( iChild = 0; iChild < psHdr->numelems; iChild++ )
621           {
622               OGRFeature *poChildFeature = NULL;
623               DGNElemCore *psChildElement;
624 
625               psChildElement = DGNReadElement( hDGN );
626               // should verify complex bit set, not another header.
627 
628               if( psChildElement != NULL )
629               {
630                   poChildFeature = ElementToFeature( psChildElement );
631                   DGNFreeElement( hDGN, psChildElement );
632               }
633 
634               if( poChildFeature != NULL
635                   && poChildFeature->GetGeometryRef() != NULL )
636               {
637                   OGRGeometry *poGeom;
638 
639                   poGeom = poChildFeature->GetGeometryRef();
640                   if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString )
641                       oChildren.addGeometry( poGeom );
642               }
643 
644               if( poChildFeature != NULL )
645                   delete poChildFeature;
646           }
647 
648           // Try to assemble into polygon geometry.
649           OGRGeometry *poGeom;
650 
651           if( psElement->type == DGNT_COMPLEX_SHAPE_HEADER )
652               poGeom = (OGRPolygon *)
653                   OGRBuildPolygonFromEdges( (OGRGeometryH) &oChildren,
654                                             TRUE, TRUE, 100000, NULL );
655           else
656               poGeom = oChildren.clone();
657 
658           if( poGeom != NULL )
659               poFeature->SetGeometryDirectly( poGeom );
660 
661           ConsiderBrush( psElement, szPen, poFeature );
662       }
663       break;
664 
665       default:
666         break;
667     }
668 
669 /* -------------------------------------------------------------------- */
670 /*      Fixup geometry dimension.                                       */
671 /* -------------------------------------------------------------------- */
672     if( poFeature->GetGeometryRef() != NULL )
673         poFeature->GetGeometryRef()->setCoordinateDimension(
674             DGNGetDimension( hDGN ) );
675 
676     return poFeature;
677 }
678 
679 /************************************************************************/
680 /*                           GetNextFeature()                           */
681 /************************************************************************/
682 
GetNextFeature()683 OGRFeature *OGRDGNLayer::GetNextFeature()
684 
685 {
686     DGNElemCore *psElement;
687 
688     DGNGetElementIndex( hDGN, NULL );
689 
690     while( (psElement = DGNReadElement( hDGN )) != NULL )
691     {
692         OGRFeature      *poFeature;
693 
694         if( psElement->deleted )
695         {
696             DGNFreeElement( hDGN, psElement );
697             continue;
698         }
699 
700         poFeature = ElementToFeature( psElement );
701         DGNFreeElement( hDGN, psElement );
702 
703         if( poFeature == NULL )
704             continue;
705 
706         if( poFeature->GetGeometryRef() == NULL )
707         {
708             delete poFeature;
709             continue;
710         }
711 
712         if( (m_poAttrQuery == NULL
713              || m_poAttrQuery->Evaluate( poFeature ))
714             && FilterGeometry( poFeature->GetGeometryRef() ) )
715             return poFeature;
716 
717         delete poFeature;
718     }
719 
720     return NULL;
721 }
722 
723 /************************************************************************/
724 /*                           TestCapability()                           */
725 /************************************************************************/
726 
TestCapability(const char * pszCap)727 int OGRDGNLayer::TestCapability( const char * pszCap )
728 
729 {
730     if( EQUAL(pszCap,OLCRandomRead) )
731         return TRUE;
732 
733     else if( EQUAL(pszCap,OLCSequentialWrite) )
734         return bUpdate;
735     else if( EQUAL(pszCap,OLCRandomWrite) )
736         return FALSE; /* maybe later? */
737 
738     else if( EQUAL(pszCap,OLCFastFeatureCount) )
739         return m_poFilterGeom == NULL || m_poAttrQuery == NULL;
740 
741     else if( EQUAL(pszCap,OLCFastSpatialFilter) )
742         return FALSE;
743 
744     else if( EQUAL(pszCap,OLCFastGetExtent) )
745         return TRUE;
746 
747     else
748         return FALSE;
749 }
750 
751 /************************************************************************/
752 /*                          GetFeatureCount()                           */
753 /************************************************************************/
754 
GetFeatureCount(int bForce)755 GIntBig OGRDGNLayer::GetFeatureCount( int bForce )
756 
757 {
758 /* -------------------------------------------------------------------- */
759 /*      If any odd conditions are in effect collect the information     */
760 /*      normally.                                                       */
761 /* -------------------------------------------------------------------- */
762     if( m_poFilterGeom != NULL || m_poAttrQuery != NULL )
763         return OGRLayer::GetFeatureCount( bForce );
764 
765 /* -------------------------------------------------------------------- */
766 /*      Otherwise scan the index.                                       */
767 /* -------------------------------------------------------------------- */
768     int nElementCount, i, nFeatureCount = 0;
769     int bInComplexShape = FALSE;
770     const DGNElementInfo *pasIndex = DGNGetElementIndex(hDGN,&nElementCount);
771 
772     for( i = 0; i < nElementCount; i++ )
773     {
774         if( pasIndex[i].flags & DGNEIF_DELETED )
775             continue;
776 
777         switch( pasIndex[i].stype )
778         {
779           case DGNST_MULTIPOINT:
780           case DGNST_ARC:
781           case DGNST_TEXT:
782             if( !(pasIndex[i].flags & DGNEIF_COMPLEX) || !bInComplexShape )
783             {
784                 nFeatureCount++;
785                 bInComplexShape = FALSE;
786             }
787             break;
788 
789           case DGNST_COMPLEX_HEADER:
790             nFeatureCount++;
791             bInComplexShape = TRUE;
792             break;
793 
794           default:
795             break;
796         }
797     }
798 
799     return nFeatureCount;
800 }
801 
802 /************************************************************************/
803 /*                             GetExtent()                              */
804 /************************************************************************/
805 
GetExtent(OGREnvelope * psExtent,CPL_UNUSED int bForce)806 OGRErr OGRDGNLayer::GetExtent( OGREnvelope *psExtent, CPL_UNUSED int bForce )
807 {
808     double      adfExtents[6];
809 
810     if( !DGNGetExtents( hDGN, adfExtents ) )
811         return OGRERR_FAILURE;
812 
813     psExtent->MinX = adfExtents[0];
814     psExtent->MinY = adfExtents[1];
815     psExtent->MaxX = adfExtents[3];
816     psExtent->MaxY = adfExtents[4];
817 
818     return OGRERR_NONE;
819 }
820 
821 /************************************************************************/
822 /*                      LineStringToElementGroup()                      */
823 /*                                                                      */
824 /*      Convert an OGR line string to one or more DGN elements.  If     */
825 /*      the input is too long for a single element (more than 38        */
826 /*      points) we split it into multiple LINE_STRING elements, and     */
827 /*      prefix with a complex group header element.                     */
828 /*                                                                      */
829 /*      This method can create handle creating shapes, or line          */
830 /*      strings for the aggregate object, but the components of a       */
831 /*      complex shape group are always line strings.                    */
832 /************************************************************************/
833 
834 #define MAX_ELEM_POINTS 38
835 
LineStringToElementGroup(OGRLineString * poLS,int nGroupType)836 DGNElemCore **OGRDGNLayer::LineStringToElementGroup( OGRLineString *poLS,
837                                                      int nGroupType )
838 
839 {
840     int nTotalPoints = poLS->getNumPoints();
841     int iNextPoint = 0, iGeom = 0;
842     DGNElemCore **papsGroup;
843 
844     papsGroup = (DGNElemCore **)
845         CPLCalloc( sizeof(void*), (nTotalPoints/(MAX_ELEM_POINTS-1))+3 );
846 
847     for( iNextPoint = 0; iNextPoint < nTotalPoints;  )
848     {
849         DGNPoint asPoints[38];
850         int nThisCount = 0;
851 
852         // we need to repeat end points of elements.
853         if( iNextPoint != 0 )
854             iNextPoint--;
855 
856         for( ; iNextPoint < nTotalPoints && nThisCount < MAX_ELEM_POINTS;
857              iNextPoint++, nThisCount++ )
858         {
859             asPoints[nThisCount].x = poLS->getX( iNextPoint );
860             asPoints[nThisCount].y = poLS->getY( iNextPoint );
861             asPoints[nThisCount].z = poLS->getZ( iNextPoint );
862         }
863 
864         if( nTotalPoints <= MAX_ELEM_POINTS )
865             papsGroup[0] = DGNCreateMultiPointElem( hDGN, nGroupType,
866                                                  nThisCount, asPoints);
867         else
868             papsGroup[++iGeom] =
869                 DGNCreateMultiPointElem( hDGN, DGNT_LINE_STRING,
870                                          nThisCount, asPoints);
871     }
872 
873 /* -------------------------------------------------------------------- */
874 /*      We needed to make into a group.  Create the complex header      */
875 /*      from the rest of the group.                                     */
876 /* -------------------------------------------------------------------- */
877     if( papsGroup[0] == NULL )
878     {
879         if( nGroupType == DGNT_SHAPE )
880             nGroupType = DGNT_COMPLEX_SHAPE_HEADER;
881         else
882             nGroupType = DGNT_COMPLEX_CHAIN_HEADER;
883 
884         papsGroup[0] =
885             DGNCreateComplexHeaderFromGroup( hDGN, nGroupType,
886                                              iGeom, papsGroup + 1 );
887     }
888 
889     return papsGroup;
890 }
891 
892 /************************************************************************/
893 /*                           TranslateLabel()                           */
894 /*                                                                      */
895 /*      Translate LABEL feature.                                        */
896 /************************************************************************/
897 
TranslateLabel(OGRFeature * poFeature)898 DGNElemCore **OGRDGNLayer::TranslateLabel( OGRFeature *poFeature )
899 
900 {
901     DGNElemCore **papsGroup;
902     OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef();
903     OGRStyleMgr oMgr;
904     OGRStyleLabel *poLabel;
905     const char *pszText = poFeature->GetFieldAsString( "Text" );
906     double dfRotation = 0.0;
907     double dfCharHeight = 100.0;
908     const char *pszFontName;
909     int nFontID = 1; // 1 is the default font for DGN. Not 0.
910 
911     oMgr.InitFromFeature( poFeature );
912     poLabel = (OGRStyleLabel *) oMgr.GetPart( 0 );
913     if( poLabel != NULL && poLabel->GetType() != OGRSTCLabel )
914     {
915         delete poLabel;
916         poLabel = NULL;
917     }
918 
919     if( poLabel != NULL )
920     {
921         GBool bDefault;
922 
923         if( poLabel->TextString(bDefault) != NULL && !bDefault )
924             pszText = poLabel->TextString(bDefault);
925         dfRotation = poLabel->Angle(bDefault);
926 
927         poLabel->Size( bDefault );
928         if( !bDefault && poLabel->GetUnit() == OGRSTUGround )
929             dfCharHeight = poLabel->Size(bDefault);
930         // this part is really kind of bogus.
931         if( !bDefault && poLabel->GetUnit() == OGRSTUMM )
932             dfCharHeight = poLabel->Size(bDefault)/1000.0;
933 
934         /* get font id */
935         static const char  *papszFontNumbers[] =
936           { "STANDARD=0", "WORKING=1", "FANCY=2", "ENGINEERING=3", "NEWZERO=4",
937             "STENCEL=5", "USTN_FANCY=7", "COMPRESSED=8", "STENCEQ=9", "hand=10",
938             "ARCH=11", "ARCHB=12", "IGES1001=15", "IGES1002=16", "IGES1003=17",
939             "CENTB=18", "MICROS=19", "ISOFRACTIONS=22", "ITALICS=23",
940             "ISO30=24", "GREEK=25", "ISOREC=26", "Isoeq=27", "ISO_FONTLEFT=30",
941             "ISO_FONTRIGHT=31", "INTL_ENGINEERING=32", "INTL_WORKING=33",
942             "ISOITEQ=34", "USTN FONT 26=36", "ARCHITECTURAL=41",
943             "BLOCK_OUTLINE=42", "LOW_RES_FILLED=43", "UPPERCASE50",
944             "FONT060=60", "din=61", "dinit=62", "helvl=63", "HELVLIT=64",
945             "helv=65", "HELVIT=66", "cent=67", "CENTIT=68", "SCRIPT=69",
946             "MICROQ=76", "dotfont=77", "DOTIT=78", "FONT092=92", "FONT094=94",
947             "ANSI_SYMBOLS=100", "FEATURE_CONTROL_SYSMBOLS=101", "SYMB_FAST=102",
948             "INTL_ISO=105", "INTL_ISO_EQUAL=106", "INTL_ISO_ITALIC=107",
949             "INTL_ISO_ITALIC_EQUAL=108", NULL };
950 
951         pszFontName = poLabel->FontName( bDefault );
952         if( !bDefault && pszFontName != NULL )
953         {
954             const char *pszFontNumber =
955                 CSLFetchNameValue((char**)papszFontNumbers, pszFontName);
956 
957             if( pszFontNumber != NULL )
958             {
959                 nFontID = atoi( pszFontNumber );
960             }
961         }
962     }
963 
964     papsGroup = (DGNElemCore **) CPLCalloc(sizeof(void*),2);
965     papsGroup[0] =
966         DGNCreateTextElem( hDGN, pszText, nFontID, DGNJ_LEFT_BOTTOM,
967                            dfCharHeight, dfCharHeight, dfRotation, NULL,
968                            poPoint->getX(),
969                            poPoint->getY(),
970                            poPoint->getZ() );
971 
972     if( poLabel )
973         delete poLabel;
974 
975     return papsGroup;
976 }
977 
978 /************************************************************************/
979 /*                           ICreateFeature()                            */
980 /*                                                                      */
981 /*      Create a new feature and write to file.                         */
982 /************************************************************************/
983 
ICreateFeature(OGRFeature * poFeature)984 OGRErr OGRDGNLayer::ICreateFeature( OGRFeature *poFeature )
985 
986 {
987     if( !bUpdate )
988     {
989         CPLError( CE_Failure, CPLE_AppDefined,
990                   "Attempt to create feature on read-only DGN file." );
991         return OGRERR_FAILURE;
992     }
993 
994     if( poFeature->GetGeometryRef() == NULL )
995     {
996         CPLError( CE_Failure, CPLE_AppDefined,
997                   "Features with empty, geometry collection geometries not\n"
998                   "supported in DGN format." );
999         return OGRERR_FAILURE;
1000     }
1001 
1002     return CreateFeatureWithGeom( poFeature, poFeature->GetGeometryRef() );
1003 }
1004 
1005 /************************************************************************/
1006 /*                       CreateFeatureWithGeom()                        */
1007 /*                                                                      */
1008 /*      Create an element or element group from a given geometry and    */
1009 /*      the given feature.  This method recurses to handle              */
1010 /*      collections as essentially independent features.                */
1011 /************************************************************************/
1012 
CreateFeatureWithGeom(OGRFeature * poFeature,OGRGeometry * poGeom)1013 OGRErr OGRDGNLayer::CreateFeatureWithGeom( OGRFeature *poFeature,
1014                                            OGRGeometry *poGeom)
1015 
1016 {
1017 /* -------------------------------------------------------------------- */
1018 /*      Translate the geometry.                                         */
1019 /* -------------------------------------------------------------------- */
1020     DGNElemCore **papsGroup = NULL;
1021     int i;
1022     const char *pszStyle = poFeature->GetStyleString();
1023 
1024     if( wkbFlatten(poGeom->getGeometryType()) == wkbPoint )
1025     {
1026         OGRPoint *poPoint = (OGRPoint *) poGeom;
1027         const char *pszText = poFeature->GetFieldAsString("Text");
1028 
1029         if( (pszText == NULL || strlen(pszText) == 0)
1030             && (pszStyle == NULL || strstr(pszStyle,"LABEL") == NULL) )
1031         {
1032             DGNPoint asPoints[2];
1033 
1034             papsGroup = (DGNElemCore **) CPLCalloc(sizeof(void*),2);
1035 
1036             // Treat a non text point as a degenerate line.
1037             asPoints[0].x = poPoint->getX();
1038             asPoints[0].y = poPoint->getY();
1039             asPoints[0].z = poPoint->getZ();
1040             asPoints[1] = asPoints[0];
1041 
1042             papsGroup[0] = DGNCreateMultiPointElem( hDGN, DGNT_LINE,
1043                                                     2, asPoints );
1044         }
1045         else
1046         {
1047             papsGroup = TranslateLabel( poFeature );
1048         }
1049     }
1050     else if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString )
1051     {
1052         papsGroup = LineStringToElementGroup( (OGRLineString *) poGeom,
1053                                               DGNT_LINE_STRING );
1054     }
1055     else if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
1056     {
1057         OGRPolygon *poPoly = ((OGRPolygon *) poGeom);
1058 
1059         DGNElemCore **papsGroupExt = LineStringToElementGroup(
1060                 poPoly->getExteriorRing(), DGNT_SHAPE);
1061 
1062         int innerRingsCnt = poPoly->getNumInteriorRings();
1063 
1064         if (innerRingsCnt > 0) {
1065             CPLDebug("InnerRings", "there are %d inner rings", innerRingsCnt);
1066             std::list<DGNElemCore*> dgnElements;
1067 
1068             for (i = 0; papsGroupExt[i] != NULL; i++) {
1069                 dgnElements.push_back(papsGroupExt[i]);
1070             }
1071             CPLFree(papsGroupExt);
1072 
1073             // get all interior rings and create complex group shape
1074             for (int iRing = 0; iRing < innerRingsCnt; iRing++) {
1075                 DGNElemCore **papsGroupInner = LineStringToElementGroup(
1076                         poPoly->getInteriorRing(iRing), DGNT_SHAPE);
1077                 papsGroupInner[0]->properties |= DGNPF_HOLE;
1078                 DGNUpdateElemCoreExtended(hDGN, papsGroupInner[0]);
1079                 for (i = 0; papsGroupInner[i] != NULL; i++) {
1080                     dgnElements.push_back(papsGroupInner[i]);
1081                 }
1082                 CPLFree(papsGroupInner);
1083             }
1084             int index = 1;
1085             papsGroup = (DGNElemCore **) CPLCalloc(sizeof(void*),
1086                     dgnElements.size() + 2);
1087             for (std::list<DGNElemCore*>::iterator list_iter =
1088                     dgnElements.begin(); list_iter != dgnElements.end();
1089                     list_iter++) {
1090                 papsGroup[index++] = *list_iter;
1091             }
1092 
1093             //papsGroup[0] = DGNCreateComplexHeaderFromGroup( hDGN, DGNT_COMPLEX_SHAPE_HEADER, dgnElements.size(), papsGroup+1);
1094             DGNPoint asPoints[1];
1095             asPoints[0].x = 0;
1096             asPoints[0].y = 0;
1097             asPoints[0].z = 0;
1098             papsGroup[0] = DGNCreateCellHeaderFromGroup(hDGN, "", 1, NULL,
1099                     dgnElements.size(), papsGroup + 1, asPoints + 0, 1.0, 1.0,
1100                     0.0);
1101             DGNAddShapeFillInfo(hDGN, papsGroup[0], 6);
1102         } else {
1103             papsGroup = papsGroupExt;
1104         }
1105     }
1106     else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon
1107              || wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint
1108              || wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString
1109              || wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
1110     {
1111         OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom;
1112         int iGeom;
1113 
1114         for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ )
1115         {
1116             OGRErr eErr = CreateFeatureWithGeom( poFeature,
1117                                                  poGC->getGeometryRef(iGeom) );
1118             if( eErr != OGRERR_NONE )
1119                 return eErr;
1120         }
1121 
1122         return OGRERR_NONE;
1123     }
1124     else
1125     {
1126         CPLError( CE_Failure, CPLE_AppDefined,
1127                   "Unsupported geometry type (%s) for DGN.",
1128                   OGRGeometryTypeToName( poGeom->getGeometryType() ) );
1129         return OGRERR_FAILURE;
1130     }
1131 
1132 /* -------------------------------------------------------------------- */
1133 /*      Add other attributes.                                           */
1134 /* -------------------------------------------------------------------- */
1135     int nLevel = poFeature->GetFieldAsInteger( "Level" );
1136     int nGraphicGroup = poFeature->GetFieldAsInteger( "GraphicGroup" );
1137     int nColor = poFeature->GetFieldAsInteger( "ColorIndex" );
1138     int nWeight = poFeature->GetFieldAsInteger( "Weight" );
1139     int nStyle = poFeature->GetFieldAsInteger( "Style" );
1140     int nMSLink = poFeature->GetFieldAsInteger( "MSLink" );
1141 
1142     nLevel = MAX(0,MIN(63,nLevel));
1143     nColor = MAX(0,MIN(255,nColor));
1144     nWeight = MAX(0,MIN(31,nWeight));
1145     nStyle = MAX(0,MIN(7,nStyle));
1146     nMSLink = MAX(0,nMSLink);
1147 
1148     DGNUpdateElemCore( hDGN, papsGroup[0], nLevel, nGraphicGroup, nColor,
1149                        nWeight, nStyle );
1150     DGNAddMSLink( hDGN, papsGroup[0], DGNLT_ODBC, 0, nMSLink );
1151 /* -------------------------------------------------------------------- */
1152 /*      Write to file.                                                  */
1153 /* -------------------------------------------------------------------- */
1154     for( i = 0; papsGroup[i] != NULL; i++ )
1155     {
1156         DGNWriteElement( hDGN, papsGroup[i] );
1157 
1158         if( i == 0 )
1159             poFeature->SetFID( papsGroup[i]->element_id );
1160 
1161         DGNFreeElement( hDGN, papsGroup[i] );
1162     }
1163 
1164     CPLFree( papsGroup );
1165 
1166     return OGRERR_NONE;
1167 }
1168