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