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