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