1 : /******************************************************************************
2 : * $Id: ogrdgnlayer.cpp 14288 2008-04-13 15:56:22Z 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 :
35 : CPL_CVSID("$Id: ogrdgnlayer.cpp 14288 2008-04-13 15:56:22Z rouault $");
36 :
37 : /************************************************************************/
38 : /* OGRDGNLayer() */
39 : /************************************************************************/
40 :
41 : 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 : void OGRDGNLayer::ConsiderBrush( DGNElemCore *psElement, const char *pszPen,
271 6 : 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 : pasPoints[i].x,
474 : 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 angle, if not horizontal
563 5 : if( psText->rotation != 0.0 )
564 : sprintf( pszOgrFS+strlen(pszOgrFS), ",a:%d",
565 0 : (int) (psText->rotation+0.5) );
566 :
567 5 : strcat( pszOgrFS, ")" );
568 :
569 5 : poFeature->SetStyleString( pszOgrFS );
570 5 : CPLFree( pszOgrFS );
571 :
572 5 : poFeature->SetField( "Text", psText->string );
573 : }
574 5 : break;
575 :
576 : case DGNST_COMPLEX_HEADER:
577 : {
578 1 : DGNElemComplexHeader *psHdr = (DGNElemComplexHeader *) psElement;
579 : int iChild;
580 1 : OGRMultiLineString oChildren;
581 :
582 : /* collect subsequent child geometries. */
583 : // we should disable the spatial filter ... add later.
584 3 : for( iChild = 0; iChild < psHdr->numelems; iChild++ )
585 : {
586 2 : OGRFeature *poChildFeature = NULL;
587 : DGNElemCore *psChildElement;
588 :
589 2 : psChildElement = DGNReadElement( hDGN );
590 : // should verify complex bit set, not another header.
591 :
592 2 : if( psChildElement != NULL )
593 : {
594 2 : poChildFeature = ElementToFeature( psChildElement );
595 2 : DGNFreeElement( hDGN, psChildElement );
596 : }
597 :
598 2 : if( poChildFeature != NULL
599 : && poChildFeature->GetGeometryRef() != NULL )
600 : {
601 : OGRGeometry *poGeom;
602 :
603 2 : poGeom = poChildFeature->GetGeometryRef();
604 2 : if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString )
605 2 : oChildren.addGeometry( poGeom );
606 : }
607 :
608 2 : if( poChildFeature != NULL )
609 2 : delete poChildFeature;
610 : }
611 :
612 : // Try to assemble into polygon geometry.
613 : OGRGeometry *poGeom;
614 :
615 1 : if( psElement->type == DGNT_COMPLEX_SHAPE_HEADER )
616 : poGeom = (OGRPolygon *)
617 : OGRBuildPolygonFromEdges( (OGRGeometryH) &oChildren,
618 0 : TRUE, TRUE, 100000, NULL );
619 : else
620 1 : poGeom = oChildren.clone();
621 :
622 1 : if( poGeom != NULL )
623 1 : poFeature->SetGeometryDirectly( poGeom );
624 :
625 1 : ConsiderBrush( psElement, szPen, poFeature );
626 : }
627 : break;
628 :
629 : default:
630 : break;
631 : }
632 :
633 : /* -------------------------------------------------------------------- */
634 : /* Fixup geometry dimension. */
635 : /* -------------------------------------------------------------------- */
636 88 : if( poFeature->GetGeometryRef() != NULL )
637 : poFeature->GetGeometryRef()->setCoordinateDimension(
638 21 : DGNGetDimension( hDGN ) );
639 :
640 88 : return poFeature;
641 : }
642 :
643 : /************************************************************************/
644 : /* GetNextFeature() */
645 : /************************************************************************/
646 :
647 16 : OGRFeature *OGRDGNLayer::GetNextFeature()
648 :
649 : {
650 : DGNElemCore *psElement;
651 :
652 16 : DGNGetElementIndex( hDGN, NULL );
653 :
654 105 : while( (psElement = DGNReadElement( hDGN )) != NULL )
655 : {
656 : OGRFeature *poFeature;
657 :
658 86 : if( psElement->deleted )
659 : {
660 0 : DGNFreeElement( hDGN, psElement );
661 0 : continue;
662 : }
663 :
664 86 : poFeature = ElementToFeature( psElement );
665 86 : DGNFreeElement( hDGN, psElement );
666 :
667 86 : if( poFeature == NULL )
668 0 : continue;
669 :
670 86 : if( poFeature->GetGeometryRef() == NULL )
671 : {
672 67 : delete poFeature;
673 67 : continue;
674 : }
675 :
676 19 : if( (m_poAttrQuery == NULL
677 : || m_poAttrQuery->Evaluate( poFeature ))
678 : && FilterGeometry( poFeature->GetGeometryRef() ) )
679 13 : return poFeature;
680 :
681 6 : delete poFeature;
682 : }
683 :
684 3 : return NULL;
685 : }
686 :
687 : /************************************************************************/
688 : /* TestCapability() */
689 : /************************************************************************/
690 :
691 0 : int OGRDGNLayer::TestCapability( const char * pszCap )
692 :
693 : {
694 0 : if( EQUAL(pszCap,OLCRandomRead) )
695 0 : return TRUE;
696 :
697 0 : else if( EQUAL(pszCap,OLCSequentialWrite) )
698 0 : return bUpdate;
699 0 : else if( EQUAL(pszCap,OLCRandomWrite) )
700 0 : return FALSE; /* maybe later? */
701 :
702 0 : else if( EQUAL(pszCap,OLCFastFeatureCount) )
703 0 : return m_poFilterGeom == NULL || m_poAttrQuery == NULL;
704 :
705 0 : else if( EQUAL(pszCap,OLCFastSpatialFilter) )
706 0 : return FALSE;
707 :
708 0 : else if( EQUAL(pszCap,OLCFastGetExtent) )
709 0 : return TRUE;
710 :
711 : else
712 0 : return FALSE;
713 : }
714 :
715 : /************************************************************************/
716 : /* GetFeatureCount() */
717 : /************************************************************************/
718 :
719 0 : int OGRDGNLayer::GetFeatureCount( int bForce )
720 :
721 : {
722 : /* -------------------------------------------------------------------- */
723 : /* If any odd conditions are in effect collect the information */
724 : /* normally. */
725 : /* -------------------------------------------------------------------- */
726 0 : if( m_poFilterGeom != NULL || m_poAttrQuery != NULL )
727 0 : return OGRLayer::GetFeatureCount( bForce );
728 :
729 : /* -------------------------------------------------------------------- */
730 : /* Otherwise scan the index. */
731 : /* -------------------------------------------------------------------- */
732 0 : int nElementCount, i, nFeatureCount = 0;
733 0 : int bInComplexShape = FALSE;
734 0 : const DGNElementInfo *pasIndex = DGNGetElementIndex(hDGN,&nElementCount);
735 :
736 0 : for( i = 0; i < nElementCount; i++ )
737 : {
738 0 : if( pasIndex[i].flags & DGNEIF_DELETED )
739 0 : continue;
740 :
741 0 : switch( pasIndex[i].stype )
742 : {
743 : case DGNST_MULTIPOINT:
744 : case DGNST_ARC:
745 : case DGNST_TEXT:
746 0 : if( !(pasIndex[i].flags & DGNEIF_COMPLEX) || !bInComplexShape )
747 : {
748 0 : nFeatureCount++;
749 0 : bInComplexShape = FALSE;
750 : }
751 0 : break;
752 :
753 : case DGNST_COMPLEX_HEADER:
754 0 : nFeatureCount++;
755 0 : bInComplexShape = TRUE;
756 : break;
757 :
758 : default:
759 : break;
760 : }
761 : }
762 :
763 0 : return nFeatureCount;
764 : }
765 :
766 : /************************************************************************/
767 : /* GetExtent() */
768 : /************************************************************************/
769 :
770 0 : OGRErr OGRDGNLayer::GetExtent( OGREnvelope *psExtent, int bForce )
771 :
772 : {
773 : double adfExtents[6];
774 :
775 0 : if( !DGNGetExtents( hDGN, adfExtents ) )
776 0 : return OGRERR_FAILURE;
777 :
778 0 : psExtent->MinX = adfExtents[0];
779 0 : psExtent->MinY = adfExtents[1];
780 0 : psExtent->MaxX = adfExtents[3];
781 0 : psExtent->MaxY = adfExtents[4];
782 :
783 0 : return OGRERR_NONE;
784 : }
785 :
786 : /************************************************************************/
787 : /* LineStringToElementGroup() */
788 : /* */
789 : /* Convert an OGR line string to one or more DGN elements. If */
790 : /* the input is too long for a single element (more than 38 */
791 : /* points) we split it into multiple LINE_STRING elements, and */
792 : /* prefix with a complex group header element. */
793 : /* */
794 : /* This method can create handle creating shapes, or line */
795 : /* strings for the aggregate object, but the components of a */
796 : /* complex shape group are always line strings. */
797 : /************************************************************************/
798 :
799 : #define MAX_ELEM_POINTS 38
800 :
801 : DGNElemCore **OGRDGNLayer::LineStringToElementGroup( OGRLineString *poLS,
802 3 : int nGroupType )
803 :
804 : {
805 3 : int nTotalPoints = poLS->getNumPoints();
806 3 : int iNextPoint = 0, iGeom = 0;
807 : DGNElemCore **papsGroup;
808 :
809 : papsGroup = (DGNElemCore **)
810 3 : CPLCalloc( sizeof(void*), (nTotalPoints/(MAX_ELEM_POINTS-1))+3 );
811 :
812 10 : for( iNextPoint = 0; iNextPoint < nTotalPoints; )
813 : {
814 : DGNPoint asPoints[38];
815 4 : int nThisCount = 0;
816 :
817 : // we need to repeat end points of elements.
818 4 : if( iNextPoint != 0 )
819 1 : iNextPoint--;
820 :
821 85 : for( ; iNextPoint < nTotalPoints && nThisCount < MAX_ELEM_POINTS;
822 : iNextPoint++, nThisCount++ )
823 : {
824 81 : asPoints[nThisCount].x = poLS->getX( iNextPoint );
825 81 : asPoints[nThisCount].y = poLS->getY( iNextPoint );
826 81 : asPoints[nThisCount].z = poLS->getZ( iNextPoint );
827 : }
828 :
829 4 : if( nTotalPoints <= MAX_ELEM_POINTS )
830 : papsGroup[0] = DGNCreateMultiPointElem( hDGN, nGroupType,
831 2 : nThisCount, asPoints);
832 : else
833 : papsGroup[++iGeom] =
834 : DGNCreateMultiPointElem( hDGN, DGNT_LINE_STRING,
835 2 : nThisCount, asPoints);
836 : }
837 :
838 : /* -------------------------------------------------------------------- */
839 : /* We needed to make into a group. Create the complex header */
840 : /* from the rest of the group. */
841 : /* -------------------------------------------------------------------- */
842 3 : if( papsGroup[0] == NULL )
843 : {
844 1 : if( nGroupType == DGNT_SHAPE )
845 0 : nGroupType = DGNT_COMPLEX_SHAPE_HEADER;
846 : else
847 1 : nGroupType = DGNT_COMPLEX_CHAIN_HEADER;
848 :
849 : papsGroup[0] =
850 : DGNCreateComplexHeaderFromGroup( hDGN, nGroupType,
851 1 : iGeom, papsGroup + 1 );
852 : }
853 :
854 3 : return papsGroup;
855 : }
856 :
857 : /************************************************************************/
858 : /* TranslateLabel() */
859 : /* */
860 : /* Translate LABEL feature. */
861 : /************************************************************************/
862 :
863 1 : DGNElemCore **OGRDGNLayer::TranslateLabel( OGRFeature *poFeature )
864 :
865 : {
866 : DGNElemCore **papsGroup;
867 1 : OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef();
868 1 : OGRStyleMgr oMgr;
869 : OGRStyleLabel *poLabel;
870 1 : const char *pszText = poFeature->GetFieldAsString( "Text" );
871 1 : double dfRotation = 0.0;
872 1 : double dfCharHeight = 100.0;
873 :
874 1 : oMgr.InitFromFeature( poFeature );
875 1 : poLabel = (OGRStyleLabel *) oMgr.GetPart( 0 );
876 1 : if( poLabel != NULL && poLabel->GetType() != OGRSTCLabel )
877 : {
878 0 : delete poLabel;
879 0 : poLabel = NULL;
880 : }
881 :
882 1 : if( poLabel != NULL )
883 : {
884 : GBool bDefault;
885 :
886 1 : if( poLabel->TextString(bDefault) != NULL && !bDefault )
887 1 : pszText = poLabel->TextString(bDefault);
888 1 : dfRotation = poLabel->Angle(bDefault);
889 :
890 1 : poLabel->Size( bDefault );
891 1 : if( !bDefault && poLabel->GetUnit() == OGRSTUGround )
892 0 : dfCharHeight = poLabel->Size(bDefault);
893 : // this part is really kind of bogus.
894 1 : if( !bDefault && poLabel->GetUnit() == OGRSTUMM )
895 1 : dfCharHeight = poLabel->Size(bDefault)/1000.0;
896 :
897 : /* poLabel->ForeColor(); */
898 :
899 : /* get font id */
900 :
901 : }
902 :
903 1 : papsGroup = (DGNElemCore **) CPLCalloc(sizeof(void*),2);
904 : papsGroup[0] =
905 : DGNCreateTextElem( hDGN, pszText, 0, DGNJ_LEFT_BOTTOM,
906 : dfCharHeight, dfCharHeight, dfRotation, NULL,
907 : poPoint->getX(),
908 : poPoint->getY(),
909 1 : poPoint->getZ() );
910 :
911 1 : if( poLabel )
912 1 : delete poLabel;
913 :
914 1 : return papsGroup;
915 : }
916 :
917 : /************************************************************************/
918 : /* CreateFeature() */
919 : /* */
920 : /* Create a new feature and write to file. */
921 : /************************************************************************/
922 :
923 4 : OGRErr OGRDGNLayer::CreateFeature( OGRFeature *poFeature )
924 :
925 : {
926 4 : if( !bUpdate )
927 : {
928 : CPLError( CE_Failure, CPLE_AppDefined,
929 0 : "Attempt to create feature on read-only DGN file." );
930 0 : return OGRERR_FAILURE;
931 : }
932 :
933 4 : if( poFeature->GetGeometryRef() == NULL )
934 : {
935 : CPLError( CE_Failure, CPLE_AppDefined,
936 : "Features with empty, geometry collection geometries not\n"
937 0 : "supported in DGN format." );
938 0 : return OGRERR_FAILURE;
939 : }
940 :
941 4 : return CreateFeatureWithGeom( poFeature, poFeature->GetGeometryRef() );
942 : }
943 :
944 : /************************************************************************/
945 : /* CreateFeatureWithGeom() */
946 : /* */
947 : /* Create an element or element group from a given geometry and */
948 : /* the given feature. This method recurses to handle */
949 : /* collections as essentially independent features. */
950 : /************************************************************************/
951 :
952 : OGRErr OGRDGNLayer::CreateFeatureWithGeom( OGRFeature *poFeature,
953 4 : OGRGeometry *poGeom)
954 :
955 : {
956 : /* -------------------------------------------------------------------- */
957 : /* Translate the geometry. */
958 : /* -------------------------------------------------------------------- */
959 4 : DGNElemCore **papsGroup = NULL;
960 : int i;
961 4 : const char *pszStyle = poFeature->GetStyleString();
962 :
963 4 : if( wkbFlatten(poGeom->getGeometryType()) == wkbPoint )
964 : {
965 1 : OGRPoint *poPoint = (OGRPoint *) poGeom;
966 1 : const char *pszText = poFeature->GetFieldAsString("Text");
967 :
968 1 : if( (pszText == NULL || strlen(pszText) == 0)
969 : && (pszStyle == NULL || strstr(pszStyle,"LABEL") == NULL) )
970 : {
971 : DGNPoint asPoints[2];
972 :
973 0 : papsGroup = (DGNElemCore **) CPLCalloc(sizeof(void*),2);
974 :
975 : // Treat a non text point as a degenerate line.
976 0 : asPoints[0].x = poPoint->getX();
977 0 : asPoints[0].y = poPoint->getY();
978 0 : asPoints[0].z = poPoint->getZ();
979 0 : asPoints[1] = asPoints[0];
980 :
981 : papsGroup[0] = DGNCreateMultiPointElem( hDGN, DGNT_LINE,
982 0 : 2, asPoints );
983 : }
984 : else
985 : {
986 1 : papsGroup = TranslateLabel( poFeature );
987 : }
988 : }
989 3 : else if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString )
990 : {
991 : papsGroup = LineStringToElementGroup( (OGRLineString *) poGeom,
992 2 : DGNT_LINE_STRING );
993 : }
994 1 : else if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
995 : {
996 1 : OGRPolygon *poPoly = ((OGRPolygon *) poGeom);
997 :
998 : // Ignore all but the exterior ring.
999 : papsGroup = LineStringToElementGroup( poPoly->getExteriorRing(),
1000 1 : DGNT_SHAPE );
1001 : }
1002 0 : else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon
1003 : || wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint
1004 : || wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString
1005 : || wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
1006 : {
1007 0 : OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom;
1008 : int iGeom;
1009 :
1010 0 : for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ )
1011 : {
1012 : OGRErr eErr = CreateFeatureWithGeom( poFeature,
1013 0 : poGC->getGeometryRef(iGeom) );
1014 0 : if( eErr != OGRERR_NONE )
1015 0 : return eErr;
1016 : }
1017 :
1018 0 : return OGRERR_NONE;
1019 : }
1020 : else
1021 : {
1022 : CPLError( CE_Failure, CPLE_AppDefined,
1023 : "Unsupported geometry type (%s) for DGN.",
1024 0 : OGRGeometryTypeToName( poGeom->getGeometryType() ) );
1025 0 : return OGRERR_FAILURE;
1026 : }
1027 :
1028 : /* -------------------------------------------------------------------- */
1029 : /* Add other attributes. */
1030 : /* -------------------------------------------------------------------- */
1031 4 : int nLevel = poFeature->GetFieldAsInteger( "Level" );
1032 4 : int nGraphicGroup = poFeature->GetFieldAsInteger( "GraphicGroup" );
1033 4 : int nColor = poFeature->GetFieldAsInteger( "ColorIndex" );
1034 4 : int nWeight = poFeature->GetFieldAsInteger( "Weight" );
1035 4 : int nStyle = poFeature->GetFieldAsInteger( "Style" );
1036 :
1037 4 : nLevel = MAX(0,MIN(63,nLevel));
1038 4 : nColor = MAX(0,MIN(255,nColor));
1039 4 : nWeight = MAX(0,MIN(31,nWeight));
1040 4 : nStyle = MAX(0,MIN(7,nStyle));
1041 :
1042 : DGNUpdateElemCore( hDGN, papsGroup[0], nLevel, nGraphicGroup, nColor,
1043 4 : nWeight, nStyle );
1044 :
1045 : /* -------------------------------------------------------------------- */
1046 : /* Write to file. */
1047 : /* -------------------------------------------------------------------- */
1048 10 : for( i = 0; papsGroup[i] != NULL; i++ )
1049 : {
1050 6 : DGNWriteElement( hDGN, papsGroup[i] );
1051 :
1052 6 : if( i == 0 )
1053 4 : poFeature->SetFID( papsGroup[i]->element_id );
1054 :
1055 6 : DGNFreeElement( hDGN, papsGroup[i] );
1056 : }
1057 :
1058 4 : CPLFree( papsGroup );
1059 :
1060 4 : return OGRERR_NONE;
1061 : }
|