001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/tools/app3d/Open3DFile.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    
037    package org.deegree.tools.app3d;
038    
039    import java.awt.Color;
040    import java.io.File;
041    import java.io.FileWriter;
042    import java.io.IOException;
043    import java.util.ArrayList;
044    import java.util.HashMap;
045    import java.util.List;
046    import java.util.Map;
047    import java.util.Set;
048    
049    import javax.media.j3d.Appearance;
050    import javax.media.j3d.BranchGroup;
051    import javax.media.j3d.ColoringAttributes;
052    import javax.media.j3d.GeometryArray;
053    import javax.media.j3d.LineArray;
054    import javax.media.j3d.Material;
055    import javax.media.j3d.PointArray;
056    import javax.media.j3d.PolygonAttributes;
057    import javax.media.j3d.RenderingAttributes;
058    import javax.media.j3d.Shape3D;
059    import javax.swing.JOptionPane;
060    import javax.swing.JProgressBar;
061    import javax.vecmath.Color3f;
062    import javax.vecmath.Point3d;
063    
064    import org.deegree.datatypes.Types;
065    import org.deegree.framework.log.ILogger;
066    import org.deegree.framework.log.LoggerFactory;
067    import org.deegree.framework.xml.XMLFragment;
068    import org.deegree.framework.xml.XSLTDocument;
069    import org.deegree.io.shpapi.shape_new.ShapeFile;
070    import org.deegree.io.shpapi.shape_new.ShapeFileReader;
071    import org.deegree.model.feature.Feature;
072    import org.deegree.model.feature.FeatureCollection;
073    import org.deegree.model.feature.FeatureProperty;
074    import org.deegree.model.feature.GMLFeatureAdapter;
075    import org.deegree.model.feature.GMLFeatureCollectionDocument;
076    import org.deegree.model.feature.schema.FeatureType;
077    import org.deegree.model.feature.schema.PropertyType;
078    import org.deegree.model.spatialschema.Curve;
079    import org.deegree.model.spatialschema.CurveSegment;
080    import org.deegree.model.spatialschema.Envelope;
081    import org.deegree.model.spatialschema.Geometry;
082    import org.deegree.model.spatialschema.GeometryException;
083    import org.deegree.model.spatialschema.MultiSurface;
084    import org.deegree.model.spatialschema.Point;
085    import org.deegree.model.spatialschema.Position;
086    import org.deegree.model.spatialschema.Ring;
087    import org.deegree.model.spatialschema.Surface;
088    import org.deegree.model.spatialschema.WKTAdapter;
089    import org.deegree.ogcbase.CommonNamespaces;
090    import org.deegree.ogcwebservices.wpvs.j3d.DefaultSurface;
091    import org.deegree.ogcwebservices.wpvs.j3d.Object3DFactory;
092    import org.deegree.ogcwebservices.wpvs.j3d.TexturedSurface;
093    import org.jdesktop.j3d.loaders.vrml97.VrmlLoader;
094    import org.w3c.dom.Node;
095    import org.w3c.dom.NodeList;
096    
097    import com.sun.j3d.loaders.Scene;
098    import com.sun.j3d.utils.geometry.GeometryInfo;
099    import com.sun.j3d.utils.geometry.NormalGenerator;
100    
101    /**
102     * <code>Open3DFile</code> reads a vrml, gml and shape files and creates a j3d scene from them.
103     *
104     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
105     * @author last edited by: $Author: mschneider $
106     *
107     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
108     */
109    public class Open3DFile {
110        private static ILogger LOG = LoggerFactory.getLogger( Open3DFile.class );
111    
112        private String fileName;
113    
114        private BranchGroup openedFile;
115    
116        private View3DFile parent;
117    
118        /**
119         * @param fileName
120         * @param parent
121         *            the frame to which message can be sent
122         */
123        public Open3DFile( String fileName, View3DFile parent ) {
124            this.fileName = fileName;
125            this.parent = parent;
126            openedFile = null;
127        }
128    
129        /**
130         * @param progressBar
131         *
132         *
133         */
134        public void openFile( final JProgressBar progressBar ) {
135    
136            try {
137                if ( fileName.toUpperCase().endsWith( ".SHP" ) ) {
138                    openedFile = readShape( fileName, progressBar );
139                } else if ( fileName.toUpperCase().endsWith( ".XML" ) || fileName.toUpperCase().endsWith( ".GML" ) ) {
140                    openedFile = readGML( fileName, progressBar );
141                } else if ( fileName.toUpperCase().endsWith( ".WRL" ) || fileName.toUpperCase().endsWith( ".VRML" ) ) {
142                    openedFile = readVRML( fileName, progressBar );
143                }
144            } catch ( IOException e ) {
145                parent.showExceptionDialog( e.getMessage() + "\nPlease, see the error log for detailed information." );
146            }
147            if ( progressBar != null ) {
148                progressBar.setValue( 100 );
149            }
150    
151        }
152    
153        /**
154         * @return the instantiated branchgroup or <code>null</code> if no branchgroup was instantiated
155         */
156        public BranchGroup getOpenedFile() {
157            return openedFile;
158        }
159    
160        private BranchGroup readShape( String fileName, JProgressBar progressBar )
161                                throws IOException {
162            try {
163                ShapeFile file = new ShapeFileReader( fileName ).read();
164                if ( progressBar != null ) {
165                    progressBar.setValue( 10 );
166                }
167                return createUniformShape3D( file.getFeatureCollection(), progressBar );
168            } catch ( Exception e ) {
169                LOG.logError( "Could not open shape file: " + fileName + " because: " + e.getMessage(), e );
170                throw new IOException( "Could not open shape file: " + fileName );
171            }
172        }
173    
174        private BranchGroup readGML( String fileName, JProgressBar progressBar )
175                                throws IOException {
176            try {
177                XMLFragment doc = new XMLFragment( new File( fileName ) );
178                boolean isCityGML = ( doc.getRootElement().getOwnerDocument().lookupPrefix(
179                                                                                            CommonNamespaces.CITYGMLNS.toASCIIString() ) != null )
180                                    || CommonNamespaces.CITYGMLNS.toASCIIString().equals(
181                                                                                          doc.getRootElement().getOwnerDocument().lookupNamespaceURI(
182                                                                                                                                                      "" ) );
183    
184                if ( !isCityGML ) {
185                    isCityGML = isCityGMLDefined( doc.getRootElement() );
186                }
187                boolean useDeegree = false;
188                if ( progressBar != null ) {
189                    progressBar.setValue( 5 );
190                }
191                if ( isCityGML ) {
192                    useDeegree = ( JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
193                                                                                            parent,
194                                                                                            "The given file contains cityGML, please choose the import method.\n 1) Load the file with all it's textures and colors.\n 2) Load the file with a uniform color.\nDue to a bug in Java3d the first option may lead to unwanted results.\n\nLoad the file with all colors and textures?",
195                                                                                            "Choose import method",
196                                                                                            JOptionPane.YES_NO_OPTION ) );
197                    XSLTDocument transformer = null;
198                    if ( useDeegree ) {
199                        transformer = new XSLTDocument( View3DFile.class.getResource( "citygmlTOgml3.xsl" ) );
200                    } else {
201                        transformer = new XSLTDocument( View3DFile.class.getResource( "toShape.xsl" ) );
202                    }
203                    doc = transformer.transform( doc );
204                }
205                if ( progressBar != null ) {
206                    progressBar.setValue( 10 );
207                }
208                GMLFeatureCollectionDocument gmlDoc = new GMLFeatureCollectionDocument();
209                gmlDoc.setRootElement( doc.getRootElement() );
210    
211                FeatureCollection fc = gmlDoc.parse();
212                if ( LOG.isDebug() ) {
213                    File f = File.createTempFile( "in_file", ".gml" );
214                    LOG.logDebug( "temp file created at: ", f );
215                    f.deleteOnExit();
216                    FileWriter fw = new FileWriter( f );
217                    fw.write( gmlDoc.getAsPrettyString() );
218                    fw.close();
219                    GMLFeatureAdapter fa = new GMLFeatureAdapter( true );
220                    GMLFeatureCollectionDocument faDoc = fa.export( fc );
221                    f = File.createTempFile( "out_file", ".gml" );
222                    f.deleteOnExit();
223                    fw = new FileWriter( f );
224                    LOG.logDebug( "temp file created at: ", f );
225                    fw.write( faDoc.getAsPrettyString() );
226                    fw.close();
227                }
228                if ( useDeegree ) {
229                    return createShape3DWithMaterial( fc, progressBar );
230                }
231                return createUniformShape3D( fc, progressBar );
232            } catch ( Exception e ) {
233                LOG.logError( "Could not open gml file: " + fileName + " because: " + e.getMessage(), e );
234                throw new IOException( "Could not open gml file: " + fileName );
235            }
236        }
237    
238        private BranchGroup readVRML( String fileName, JProgressBar progressBar )
239                                throws IOException {
240            VrmlLoader loader = new VrmlLoader();
241            try {
242                if ( progressBar != null ) {
243                    progressBar.setValue( 10 );
244                }
245                Scene scene = loader.load( fileName );
246                if ( scene != null ) {
247                    if ( progressBar != null ) {
248                        progressBar.setValue( 60 );
249                        Thread.sleep( 50 );
250                    }
251                    BranchGroup bg = scene.getSceneGroup();
252                    bg.setCapability( BranchGroup.ALLOW_DETACH );
253                    if ( progressBar != null ) {
254                        progressBar.setValue( 90 );
255                        Thread.sleep( 50 );
256                    }
257                    return bg;
258                }
259                throw new Exception( "Could not create scene from file: " + fileName );
260            } catch ( Exception e ) {
261                LOG.logError( "Error while loading vrml from file: " + fileName, e );
262                throw new IOException( "Could not create vrml scene from file: " + fileName + " because: " + e.getMessage() );
263            }
264        }
265    
266        /**
267         * @param contextNode
268         * @return true if the namespace "http://www.citygml.org/citygml/1/0/0" was found in one of the nodes of the
269         *         dom-tree.
270         */
271        private boolean isCityGMLDefined( Node contextNode ) {
272    
273            boolean isCityGML = ( contextNode.lookupPrefix( CommonNamespaces.CITYGMLNS.toASCIIString() ) != null )
274                                || CommonNamespaces.CITYGMLNS.toASCIIString().equals( contextNode.lookupNamespaceURI( null ) );
275            if ( !isCityGML ) {
276                NodeList nl = contextNode.getChildNodes();
277                for ( int i = 0; i < nl.getLength(); ++i ) {
278                    isCityGML = isCityGMLDefined( nl.item( i ) );
279                    if ( isCityGML ) {
280                        return true;
281                    }
282                }
283            }
284            return isCityGML;
285        }
286    
287        private Shape3D mapGeometryToShape3D( Geometry geom, Point3d translation ) {
288            if ( geom instanceof Point ) {
289                return createShape3D( (Point) geom, translation );
290            } else if ( geom instanceof Curve ) {
291                return createShape3D( (Curve) geom, translation );
292            } else if ( geom instanceof Surface ) {
293                return createShape3D( (Surface) geom, translation );
294            } else if ( geom instanceof MultiSurface ) {
295                return createShape3D( (MultiSurface) geom, translation );
296            } else {
297                if ( geom == null ) {
298                    LOG.logError( "Could not map the geometry which was not instantiated" );
299                } else {
300                    LOG.logError( "Could not map the geometry: " + geom.getClass().getName() );
301                }
302                return null;
303            }
304        }
305    
306        private Shape3D createShape3D( Point p, Point3d translation ) {
307            GeometryArray geomArray = new PointArray( 1, GeometryArray.COORDINATES );
308            double z = p.getZ();
309            if ( Double.isInfinite( z ) || Double.isNaN( z ) ) {
310                z = 0;
311            }
312            geomArray.setCoordinate( 0, new Point3d( p.getX() + translation.x, p.getY() + translation.y, z + translation.z ) );
313            Shape3D result = new Shape3D( geomArray );
314            result.setAppearanceOverrideEnable( true );
315            return result;
316        }
317    
318        private Shape3D createShape3D( Curve c, Point3d translation ) {
319            int totalPoints = 0;
320            List<Integer> failSegments = new ArrayList<Integer>();
321            for ( int i = 0; i < c.getNumberOfCurveSegments(); ++i ) {
322                try {
323                    totalPoints += c.getCurveSegmentAt( i ).getNumberOfPoints();
324                } catch ( GeometryException e ) {
325                    LOG.logError( "Could not get CurveSegment at position: " + i );
326                    failSegments.add( new Integer( i ) );
327                }
328            }
329    
330            LineArray geomArray = new LineArray( totalPoints, GeometryArray.COORDINATES );
331            for ( int i = 0, pointCounter = 0; i < c.getNumberOfCurveSegments(); ++i ) {
332                if ( !failSegments.contains( new Integer( i ) ) ) {
333                    CurveSegment segment = null;
334                    try {
335                        segment = c.getCurveSegmentAt( i );
336                    } catch ( GeometryException e ) {
337                        // cannot happen.
338                    }
339                    for ( int k = 0; k < segment.getNumberOfPoints(); ++k ) {
340                        Position p = segment.getPositionAt( k );
341                        double z = p.getZ();
342                        if ( Double.isInfinite( z ) || Double.isNaN( z ) ) {
343                            z = 0;
344                        }
345                        geomArray.setCoordinate( pointCounter++, new Point3d( p.getX() + translation.x, p.getY()
346                                                                                                        + translation.y,
347                                                                              z + translation.z ) );
348                    }
349                }
350            }
351            Shape3D result = new Shape3D( geomArray );
352            result.setAppearanceOverrideEnable( true );
353            return result;
354        }
355    
356        /**
357         *
358         * @param surface
359         *            to be created
360         * @param translation
361         *            to origin of the scene
362         * @return a Shape3D created from the surface.
363         */
364        private Shape3D createShape3D( Surface surface, Point3d translation ) {
365            GeometryInfo geometryInfo = new GeometryInfo( GeometryInfo.POLYGON_ARRAY );
366            Position[] pos = surface.getSurfaceBoundary().getExteriorRing().getPositions();
367            Ring[] innerRings = surface.getSurfaceBoundary().getInteriorRings();
368            int numberOfRings = 1;
369            int numberOfCoordinates = 3 * ( pos.length                   );
370            if ( innerRings != null ) {
371                for ( int i = 0; i < innerRings.length; i++ ) {
372                    numberOfRings++;
373                    numberOfCoordinates += ( 3 * innerRings[i].getPositions().length );
374                }
375            }
376    
377            float[] coords = new float[numberOfCoordinates];
378            int contourCounts[] = { numberOfRings };
379            int[] stripCounts = new int[numberOfRings];
380            numberOfRings = 0;
381            stripCounts[numberOfRings++] = pos.length;
382    
383            int z = 0;
384            for ( int i = 0; i < pos.length; i++ ) {
385                double zValue = pos[i].getZ();
386                if ( Double.isInfinite( zValue ) || Double.isNaN( zValue ) ) {
387                    zValue = 0;
388                }
389                // LOG.logDebug( "Found a point in a surface: " + pos[i] );
390                coords[z++] = (float) ( pos[i].getX() + translation.x );
391                coords[z++] = (float) ( pos[i].getY() + translation.y );
392                coords[z++] = (float) ( zValue + translation.z );
393            }
394    
395            if ( innerRings != null ) {
396                for ( int j = 0; j < innerRings.length; j++ ) {
397                    pos = innerRings[j].getPositions();
398                    stripCounts[numberOfRings++] = pos.length;
399                    for ( int i = 0; i < pos.length; i++ ) {
400                        double zValue = pos[i].getZ();
401                        if ( Double.isInfinite( zValue ) || Double.isNaN( zValue ) ) {
402                            zValue = 0;
403                        }
404                        coords[z++] = (float) ( pos[i].getX() + translation.x );
405                        coords[z++] = (float) ( pos[i].getY() + translation.y );
406                        coords[z++] = (float) ( zValue + translation.z );
407                    }
408                }
409            }
410            geometryInfo.setCoordinates( coords );
411            geometryInfo.setStripCounts( stripCounts );
412            geometryInfo.setContourCounts( contourCounts );
413            geometryInfo.recomputeIndices();
414    
415            NormalGenerator ng = new NormalGenerator();
416            ng.generateNormals( geometryInfo );
417            Shape3D result = new Shape3D( geometryInfo.getGeometryArray() );
418            result.setCapability( Shape3D.ALLOW_GEOMETRY_READ );
419            result.setCapability( Shape3D.ALLOW_GEOMETRY_WRITE );
420            result.setAppearanceOverrideEnable( true );
421            return result;
422        }
423    
424        /**
425         * @param multiSurface
426         * @param translation
427         * @return a Shape3D created from the multisurfaces.
428         */
429        private Shape3D createShape3D( MultiSurface multiSurface, Point3d translation ) {
430            LOG.logDebug( "Found a Multi surface" );
431            Shape3D result = new Shape3D();
432            result.setCapability( Shape3D.ALLOW_GEOMETRY_READ );
433            result.setCapability( Shape3D.ALLOW_GEOMETRY_WRITE );
434            Surface[] allSurfaces = multiSurface.getAllSurfaces();
435            for ( int surfaceCount = 0; surfaceCount < allSurfaces.length; ++surfaceCount ) {
436                Surface surface = multiSurface.getSurfaceAt( surfaceCount );
437                Shape3D s3D = createShape3D( surface, translation );
438                result.addGeometry( s3D.getGeometry() );
439            }
440            result.setAppearanceOverrideEnable( true );
441            return result;
442        }
443    
444        /**
445         * This method recursively constructs all the surfaces contained in the given feature. If the Feature contains a
446         * PropertyType of {@link Types#FEATURE} this Feature will also be traversed, if it contains a
447         * {@link Types#GEOMETRY} a {@link DefaultSurface} will be created.
448         *
449         * @param o3DFactory
450         *            the Factory to create the defaultservice
451         * @param feature
452         *            the feature to traverse.
453         */
454        private void createSurfaces( Object3DFactory o3DFactory, Feature feature, Map<String, TexturedSurface> textureMap,
455                                     BranchGroup result ) {
456    
457            FeatureType ft = feature.getFeatureType();
458            PropertyType[] propertyTypes = ft.getProperties();
459            for ( PropertyType pt : propertyTypes ) {
460                if ( pt.getType() == Types.FEATURE ) {
461                    FeatureProperty[] fp = feature.getProperties( pt.getName() );
462                    if ( fp != null ) {
463                        for ( int i = 0; i < fp.length; i++ ) {
464                            createSurfaces( o3DFactory, (Feature) fp[i].getValue(), textureMap, result );
465                        }
466                    }
467                } else if ( pt.getType() == Types.GEOMETRY ) {
468                    DefaultSurface ds = o3DFactory.createSurface( feature, textureMap );
469                    if ( ds != null ) {
470                        ds.compile();
471                        result.addChild( ds );
472                        // resolutionStripe.addFeature( id + "_" + ds.getDefaultSurfaceID(), ds );
473                    }
474                }
475            }
476        }
477    
478        private BranchGroup createUniformShape3D( FeatureCollection fc, JProgressBar progressBar ) {
479            BranchGroup bg = new BranchGroup();
480            bg.setCapability( BranchGroup.ALLOW_DETACH );
481            Appearance app = new Appearance();
482            RenderingAttributes ra = new RenderingAttributes();
483            ra.setDepthBufferEnable( true );
484            app.setRenderingAttributes( ra );
485            Material material = new Material();
486            material.setAmbientColor( new Color3f( Color.WHITE ) );
487            material.setDiffuseColor( new Color3f( Color.RED ) );
488            material.setSpecularColor( new Color3f( Color.BLUE ) );
489            ColoringAttributes ca = new ColoringAttributes();
490            ca.setShadeModel( ColoringAttributes.SHADE_GOURAUD );
491            ca.setCapability( ColoringAttributes.NICEST );
492            app.setColoringAttributes( ca );
493            PolygonAttributes pa = new PolygonAttributes();
494            pa.setCullFace( PolygonAttributes.CULL_NONE );
495            pa.setBackFaceNormalFlip( true );
496            pa.setPolygonMode( PolygonAttributes.POLYGON_FILL );
497            app.setPolygonAttributes( pa );
498            app.setMaterial( material );
499            Envelope bbox = null;
500            for ( int i = 0; i < fc.size(); ++i ) {
501                Feature f = fc.getFeature( i );
502                Geometry geom = f.getDefaultGeometryPropertyValue();
503                if ( LOG.isDebug() ) {
504                    try {
505                        LOG.logDebug( "Found geometry: " + WKTAdapter.export( geom ) );
506                    } catch ( GeometryException e ) {
507                        LOG.logError( e.getMessage(), e );
508                    }
509    
510                }
511                if ( bbox == null ) {
512                    bbox = geom.getEnvelope();
513                } else {
514                    try {
515                        bbox = bbox.merge( geom.getEnvelope() );
516                        LOG.logDebug( "merging the bboxes resulted in: " + bbox );
517                    } catch ( GeometryException e ) {
518                        LOG.logError( "Couldn't merge the bboxes of the found featureCollection", e );
519                    }
520                }
521            }
522            if ( progressBar != null ) {
523                progressBar.setValue( 20 );
524            }
525            Point3d centroid = new Point3d( 0, 0, 0 );
526            if ( bbox != null ) {
527                Point p = bbox.getCentroid();
528                double zValue = p.getZ();
529                if ( Double.isInfinite( zValue ) || Double.isNaN( zValue ) ) {
530                    zValue = 0;
531                }
532                centroid.set( -p.getX(), -p.getY(), -zValue );
533            }
534            final double progress = ( (double) ( ( progressBar.getMaximum() - 10 ) - progressBar.getValue() ) ) / fc.size();
535            double currentProg = progressBar.getValue();
536            for ( int i = 0; i < fc.size(); ++i ) {
537                Feature f = fc.getFeature( i );
538                if ( progressBar != null ) {
539                    currentProg += progress;
540                    if ( ( (int) Math.floor( currentProg ) - progressBar.getValue() ) > 5 ) {
541                        progressBar.setValue( (int) Math.floor( currentProg ) );
542                    }
543                }
544                Geometry geom = f.getDefaultGeometryPropertyValue();
545                Shape3D shape = mapGeometryToShape3D( geom, centroid );
546                if ( shape != null ) {
547                    shape.setAppearance( app );
548                    bg.addChild( shape );
549                } else {
550                    // System.out.println( "ERRORORORORORORO" );
551                }
552            }
553            if ( !bg.getAllChildren().hasMoreElements() ) {
554                throw new IllegalArgumentException( "Could not read any 3D-Info from given featurecollection." );
555            }
556            return bg;
557        }
558    
559        /**
560         * Use the Object3DFactory of the wpvs to create colored surfaces and textures.
561         *
562         * @param fc
563         * @return the branch group
564         */
565        private BranchGroup createShape3DWithMaterial( FeatureCollection fc, JProgressBar progressBar ) {
566            BranchGroup bg = new BranchGroup();
567            bg.setCapability( BranchGroup.ALLOW_DETACH );
568    
569            Object3DFactory o3DFactory = new Object3DFactory();
570            Map<String, TexturedSurface> textureMap = new HashMap<String, TexturedSurface>( fc.size() * 10 );
571            final double progress = ( (double) ( ( progressBar.getMaximum() - 10 ) - progressBar.getValue() ) ) / fc.size();
572            double currentProg = progressBar.getValue();
573            for ( int i = 0; i < fc.size(); ++i ) {
574                if ( progressBar != null ) {
575                    currentProg += progress;
576                    if ( ( (int) Math.floor( currentProg ) - progressBar.getValue() ) > 5 ) {
577                        progressBar.setValue( (int) Math.floor( currentProg ) );
578                    }
579                }
580                Feature feature = fc.getFeature( i );
581                createSurfaces( o3DFactory, feature, textureMap, bg );
582            }
583            if ( textureMap.size() > 0 ) {
584                Set<String> keys = textureMap.keySet();
585                for ( String key : keys ) {
586                    if ( key != null ) {
587                        TexturedSurface surf = textureMap.get( key );
588                        if ( surf != null ) {
589                            surf.compile();
590                            bg.addChild( surf );
591                        }
592                    }
593    
594                }
595            }
596            if ( !bg.getAllChildren().hasMoreElements() ) {
597                throw new IllegalArgumentException( "Could not read any 3D-Info from given featurecollection." );
598            }
599            return bg;
600        }
601    }