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 }