001    //$HeadURL: svn+ssh://mschneider@svn.wald.intevation.org/deegree/base/trunk/resources/eclipse/svn_classfile_header_template.xml $
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.BorderLayout;
040    import java.awt.Color;
041    import java.awt.Dimension;
042    import java.awt.GraphicsConfigTemplate;
043    import java.awt.GridBagConstraints;
044    import java.awt.GridBagLayout;
045    import java.awt.Insets;
046    import java.awt.event.ActionEvent;
047    import java.awt.event.ActionListener;
048    import java.awt.event.KeyEvent;
049    import java.awt.event.KeyListener;
050    import java.awt.event.WindowAdapter;
051    import java.awt.event.WindowEvent;
052    import java.io.File;
053    import java.io.FileWriter;
054    import java.io.IOException;
055    import java.security.InvalidParameterException;
056    import java.util.ArrayList;
057    import java.util.Enumeration;
058    import java.util.List;
059    import java.util.prefs.Preferences;
060    
061    import javax.media.j3d.Appearance;
062    import javax.media.j3d.Background;
063    import javax.media.j3d.BoundingBox;
064    import javax.media.j3d.BoundingSphere;
065    import javax.media.j3d.Bounds;
066    import javax.media.j3d.BranchGroup;
067    import javax.media.j3d.Canvas3D;
068    import javax.media.j3d.ColoringAttributes;
069    import javax.media.j3d.DirectionalLight;
070    import javax.media.j3d.GraphicsConfigTemplate3D;
071    import javax.media.j3d.Group;
072    import javax.media.j3d.Light;
073    import javax.media.j3d.Material;
074    import javax.media.j3d.RenderingAttributes;
075    import javax.media.j3d.Texture;
076    import javax.media.j3d.Transform3D;
077    import javax.media.j3d.TransformGroup;
078    import javax.media.j3d.View;
079    import javax.swing.BorderFactory;
080    import javax.swing.JButton;
081    import javax.swing.JDialog;
082    import javax.swing.JFileChooser;
083    import javax.swing.JFrame;
084    import javax.swing.JLabel;
085    import javax.swing.JOptionPane;
086    import javax.swing.JPanel;
087    import javax.swing.JProgressBar;
088    import javax.swing.JRadioButton;
089    import javax.swing.SwingConstants;
090    import javax.swing.WindowConstants;
091    import javax.swing.border.BevelBorder;
092    import javax.swing.border.Border;
093    import javax.swing.filechooser.FileFilter;
094    import javax.vecmath.Color3f;
095    import javax.vecmath.Point3d;
096    import javax.vecmath.Vector3d;
097    import javax.vecmath.Vector3f;
098    
099    import org.deegree.framework.log.ILogger;
100    import org.deegree.framework.log.LoggerFactory;
101    import org.j3d.renderer.java3d.geom.Sphere;
102    import org.w3c.dom.Node;
103    
104    import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
105    import com.sun.j3d.utils.geometry.ColorCube;
106    import com.sun.j3d.utils.universe.SimpleUniverse;
107    
108    /**
109     * The <code>View3DFile</code> class can display shape and gml/citygml file in 3D.
110     *
111     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
112     *
113     * @author last edited by: $Author:$
114     *
115     * @version $Revision:$, $Date:$
116     *
117     */
118    
119    public class View3DFile extends JFrame implements ActionListener, KeyListener {
120        /**
121         *
122         */
123        private static final long serialVersionUID = 7698388852544865855L;
124    
125        private static ILogger LOG = LoggerFactory.getLogger( View3DFile.class );
126    
127        private SimpleUniverse simpleUniverse;
128    
129        private Canvas3D canvas;
130    
131        private MouseRotate trackBall;
132    
133        private BranchGroup scene;
134    
135        private TransformGroup rotationGroup;
136    
137        private Light firstLight, secondLight, thirdLight;
138    
139        private Point3d centroid;
140    
141        private Preferences prefs;
142    
143        private final static String OPEN_KEY = "lastOpenLocation";
144    
145        private final static String LAST_EXTENSION = "lastFileExtension";
146    
147        private final static String WIN_TITLE = "Deegree 3D Object viewer: ";
148    
149        private Background backGround;
150    
151        private double zoomX = 1;
152    
153        private double zoomY = 1;
154    
155        private double zoomZ = 1;
156    
157        private final Vector3d upVector = new Vector3d( 0, 1, 0 );
158    
159        private List<CustomFileFilter> supportedOpenFilter = new ArrayList<CustomFileFilter>();
160    
161        /**
162         * A panel showing some key stroke helps
163         */
164        JPanel helpLister;
165    
166        /**
167         * Creates a frame with the menus and the canvas3d set and tries to load the file from given location.
168         *
169         * @param fileName
170         *            to be loaded.
171         */
172        public View3DFile( String fileName ) {
173            this( false );
174            readFile( fileName );
175        }
176    
177        /**
178         * Creates a new frame with the menus and the canvas3d set.
179         *
180         * @param testSphere
181         *            true if a sphere should be displayed.
182         */
183        public View3DFile( boolean testSphere ) {
184            super( WIN_TITLE );
185            prefs = Preferences.userNodeForPackage( View3DFile.class );
186            setupGUI();
187    
188            // openFileChooser();
189    
190            setupJava3D( testSphere );
191            ArrayList<String> extensions = new ArrayList<String>();
192    
193            extensions.add( "gml" );
194            extensions.add( "xml" );
195            supportedOpenFilter.add( new CustomFileFilter( extensions, "(*.gml, *.xml) GML or CityGML-Files" ) );
196    
197            extensions.clear();
198            extensions.add( "shp" );
199            supportedOpenFilter.add( new CustomFileFilter( extensions, "(*.shp) Esri ShapeFiles" ) );
200    
201            extensions.clear();
202            extensions.add( "vrml" );
203            extensions.add( "wrl" );
204            supportedOpenFilter.add( new CustomFileFilter( extensions,
205                                                           "(*.vrml, *.wrl) VRML97 - Virtual Reality Modelling Language" ) );
206    
207            pack();
208        }
209    
210        /**
211         * GUI stuff
212         */
213        private void setupGUI() {
214            // add listener for closing the frame/application
215            addWindowListener( new WindowAdapter() {
216                @Override
217                public void windowClosing( WindowEvent evt ) {
218                    System.exit( 0 );
219                }
220            } );
221            setLayout( new BorderLayout() );
222            setMinimumSize( new Dimension( 600, 600 ) );
223            setPreferredSize( new Dimension( 600, 600 ) );
224            setVisible( true );
225            // Adding the button panel
226            JPanel totalPanel = new JPanel( new BorderLayout() );
227            // JPanel buttonPanel = new JPanel( new FlowLayout() );
228            // createButtons( buttonPanel );
229            totalPanel.add( createButtons(), BorderLayout.NORTH );
230            addKeyListener( this );
231            helpLister = new JPanel( new GridBagLayout() );
232            Border border = BorderFactory.createTitledBorder( BorderFactory.createBevelBorder( BevelBorder.LOWERED ),
233                                                              "Instant help" );
234            helpLister.setBorder( border );
235            GridBagConstraints gb = new GridBagConstraints();
236            gb.ipadx = 10;
237            gb.gridx = 0;
238            gb.gridy = 0;
239            JLabel tmp = new JLabel( "x: move postive X-axis" );
240            helpLister.add( tmp, gb );
241    
242            gb.gridx++;
243            tmp = new JLabel( "X: move negative X-axis" );
244            helpLister.add( tmp, gb );
245    
246            gb.gridx = 0;
247            gb.gridy++;
248            tmp = new JLabel( "y: move positve Y-axis" );
249            helpLister.add( tmp, gb );
250    
251            gb.gridx++;
252            tmp = new JLabel( "Y: move negative Y-axis" );
253            helpLister.add( tmp, gb );
254    
255            gb.gridy++;
256            gb.gridx = 0;
257            tmp = new JLabel( "z: move positve Z-axis" );
258            helpLister.add( tmp, gb );
259            gb.gridx++;
260    
261            tmp = new JLabel( "Z: move negative Z-axis" );
262            helpLister.add( tmp, gb );
263            helpLister.setVisible( false );
264    
265            totalPanel.add( helpLister, BorderLayout.SOUTH );
266            getContentPane().add( totalPanel, BorderLayout.SOUTH );
267    
268        }
269    
270        private JFileChooser createFileChooser( List<CustomFileFilter> fileFilter ) {
271            // Setting up the fileChooser.
272    
273            String lastLoc = prefs.get( OPEN_KEY, System.getProperty( "user.home" ) );
274    
275            File lastFile = new File( lastLoc );
276            if ( !lastFile.exists() ) {
277                lastFile = new File( System.getProperty( "user.home" ) );
278            }
279            JFileChooser fileChooser = new JFileChooser( lastFile );
280            fileChooser.setMultiSelectionEnabled( false );
281            if ( fileFilter != null && fileFilter.size() > 0 ) {
282                // the *.* file fileter is off
283                fileChooser.setAcceptAllFileFilterUsed( false );
284                String lastExtension = prefs.get( LAST_EXTENSION, "*" );
285                FileFilter selected = fileFilter.get( 0 );
286                for ( CustomFileFilter filter : fileFilter ) {
287                    fileChooser.setFileFilter( filter );
288                    if ( filter.accepts( lastExtension ) ) {
289                        selected = filter;
290                    }
291                }
292    
293                fileChooser.setFileFilter( selected );
294            }
295    
296            return fileChooser;
297        }
298    
299        private void setupJava3D( boolean testSphere ) {
300            // setting up Java3D
301            GraphicsConfigTemplate3D configTemplate = new GraphicsConfigTemplate3D();
302            configTemplate.setSceneAntialiasing( GraphicsConfigTemplate.PREFERRED );
303            configTemplate.setDoubleBuffer( GraphicsConfigTemplate.REQUIRED );
304            canvas = new Canvas3D( SimpleUniverse.getPreferredConfiguration() );
305            canvas.setDoubleBufferEnable( true );
306            simpleUniverse = new SimpleUniverse( canvas );
307            if ( canvas != null ) {
308                getContentPane().add( canvas, BorderLayout.CENTER );
309            }
310    
311            View view = simpleUniverse.getViewer().getView();
312    
313            // view parameters
314            view.setSceneAntialiasingEnable( true );
315            view.setUserHeadToVworldEnable( true );
316    
317            centroid = new Point3d( 0.3, 0.3, 0.3 );
318            scene = new BranchGroup();
319            firstLight = createDirectionalLight( new Vector3f( 0, 0, 1 ) );
320            secondLight = createDirectionalLight( new Vector3f( 0, -1, -1 ) );
321            thirdLight = createDirectionalLight( new Vector3f( -1, 0, 0 ) );
322    
323            scene.addChild( firstLight );
324            scene.addChild( secondLight );
325            scene.addChild( thirdLight );
326    
327            // is handled by the mouse rotater, all objects will be added to it.
328            rotationGroup = new TransformGroup();
329            rotationGroup.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );
330            rotationGroup.setCapability( TransformGroup.ALLOW_TRANSFORM_READ );
331            rotationGroup.setCapability( BranchGroup.ALLOW_DETACH );
332            rotationGroup.setCapability( Group.ALLOW_CHILDREN_EXTEND );
333            rotationGroup.setCapability( Group.ALLOW_CHILDREN_READ );
334            rotationGroup.setCapability( Group.ALLOW_CHILDREN_WRITE );
335            scene.addChild( rotationGroup );
336    
337            backGround = new Background( new Color3f( Color.LIGHT_GRAY ) );
338            backGround.setCapability( javax.media.j3d.Node.ALLOW_BOUNDS_WRITE );
339            backGround.setCapability( javax.media.j3d.Node.ALLOW_BOUNDS_READ );
340            backGround.setCapability( Background.ALLOW_APPLICATION_BOUNDS_READ );
341            backGround.setCapability( Background.ALLOW_APPLICATION_BOUNDS_WRITE );
342    
343            scene.addChild( backGround );
344    
345            trackBall = new MouseRotate( rotationGroup );
346            scene.addChild( trackBall );
347    
348            simpleUniverse.addBranchGraph( scene );
349    
350            // adding the key listeners
351            canvas.addKeyListener( this );
352    
353            if ( testSphere ) {
354                BranchGroup sphere = createStartScene( null );
355                addBranchGroupToScene( sphere );
356            }
357        }
358    
359        /**
360         * Create a directional light, with color.WHITE.
361         */
362        private DirectionalLight createDirectionalLight( Vector3f lightDir ) {
363            // create the color for the light
364            Color3f color = new Color3f( Color.WHITE );
365            // create the directional light with the color and direction
366            DirectionalLight light = new DirectionalLight( color, lightDir );
367            light.setCapability( Light.ALLOW_INFLUENCING_BOUNDS_READ );
368            light.setCapability( Light.ALLOW_INFLUENCING_BOUNDS_WRITE );
369            return light;
370        }
371    
372        /**
373         * Add the given branch group to the scene and set the appropriate distance etc. After adding the branch group to
374         * the rotation group which is controlled by the mouse rotator.
375         *
376         * @param b
377         */
378        private void addBranchGroupToScene( BranchGroup b ) {
379            rotationGroup.removeAllChildren();
380            // translationGroup.removeAllChildren();
381            // System.out.println( b.getBounds() );
382            Bounds bounds = b.getBounds();
383            BranchGroup wrapper = new BranchGroup();
384            wrapper.setCapability( BranchGroup.ALLOW_DETACH );
385            wrapper.setCapability( Group.ALLOW_CHILDREN_READ );
386            wrapper.setCapability( Texture.ALLOW_ENABLE_READ );
387            wrapper.setCapability( Texture.ALLOW_IMAGE_READ );
388            if ( bounds != null ) {
389                LOG.logDebug( "Old centroid: " + centroid );
390                BoundingSphere bs = new BoundingSphere( bounds );
391                bs.getCenter( centroid );
392                LOG.logDebug( "New centroid: " + centroid );
393    
394                TransformGroup viewToWorld = simpleUniverse.getViewingPlatform().getViewPlatformTransform();
395                Transform3D trans = new Transform3D();
396    
397                BoundingBox bbox = new BoundingBox( bounds );
398                Point3d lower = new Point3d();
399                bbox.getLower( lower );
400                Point3d upper = new Point3d();
401                bbox.getUpper( upper );
402                double lengthX = Math.abs( upper.x - lower.x );
403                double lengthY = Math.abs( upper.y - lower.y );
404                double lengthZ = Math.abs( upper.z - lower.z );
405    
406                zoomX = lengthX * 0.1;
407                zoomY = lengthY * 0.1;
408                zoomZ = lengthZ * 0.1;
409    
410                double radius = ( lengthX > lengthY ) ? ( ( lengthX > lengthZ ) ? lengthX
411                                                                               : ( ( lengthY > lengthZ ) ? lengthY
412                                                                                                        : lengthZ ) )
413                                                     : ( ( lengthY > lengthZ ) ? lengthY : lengthZ );
414                Point3d eye = new Point3d();
415                eye.z += ( ( lower.z - upper.z < 0 ) ? -1 : 1 ) * ( radius * 1.5 );
416                if ( Math.abs( eye.z ) < 0.00001 && Math.abs( centroid.z ) < 0.00001 ) {
417                    eye.z = 1;
418                }
419                trans.lookAt( eye, centroid, upVector );
420    
421                LOG.logDebug( "Trans Matrix after lookat:\n" + trans );
422                LOG.logDebug( "BBox lower: " + lower );
423                LOG.logDebug( "BBox upper: " + upper );
424                LOG.logDebug( "Center: " + centroid );
425                LOG.logDebug( "eye: " + eye );
426                LOG.logDebug( "up: " + upVector );
427                LOG.logDebug( "radius: " + bs.getRadius() );
428                LOG.logDebug( "used radius (of largest axis): " + radius );
429                LOG.logDebug( "zoomX: " + zoomX );
430                LOG.logDebug( "zoomY: " + zoomY );
431                LOG.logDebug( "zoomZ: " + zoomZ );
432                viewToWorld.setTransform( trans );
433                View view = simpleUniverse.getViewer().getView();
434    
435                // view parameters
436                view.setBackClipDistance( radius * 100 );
437                // the near clippingplane is one hundereth of the far.
438                view.setFrontClipDistance( radius * 0.001 );
439    
440                BoundingSphere influence = new BoundingSphere( eye, radius * 4 );
441    
442                firstLight.setInfluencingBounds( influence );
443                secondLight.setInfluencingBounds( influence );
444                thirdLight.setInfluencingBounds( influence );
445                trackBall.setSchedulingBounds( influence );
446                backGround.setApplicationBounds( influence );
447    
448                // add the negativ translation for the mouse tracker to work.
449                TransformGroup resultTrans = new TransformGroup();
450                Transform3D negativTranslation = new Transform3D();
451                negativTranslation.setTranslation( new Vector3d( -centroid.x, -centroid.y, -centroid.z ) );
452                resultTrans.setTransform( negativTranslation );
453                resultTrans.addChild( b );
454                wrapper.addChild( resultTrans );
455            } else {
456                wrapper.addChild( b );
457            }
458            rotationGroup.addChild( wrapper );
459        }
460    
461        /**
462         * @return a shere and a cube
463         */
464        private BranchGroup createStartScene( Point3d translation ) {
465            Appearance app = new Appearance();
466            RenderingAttributes ra = new RenderingAttributes();
467            ra.setDepthBufferEnable( true );
468            ra.setDepthBufferWriteEnable( true );
469            app.setRenderingAttributes( ra );
470    
471            ColoringAttributes ca = new ColoringAttributes();
472            ca.setShadeModel( ColoringAttributes.SHADE_GOURAUD );
473            ca.setCapability( ColoringAttributes.NICEST );
474            app.setColoringAttributes( ca );
475    
476            Material material = new Material();
477            material.setAmbientColor( new Color3f( Color.WHITE ) );
478            material.setDiffuseColor( new Color3f( Color.RED ) );
479            material.setSpecularColor( new Color3f( Color.ORANGE ) );
480            app.setMaterial( material );
481            TransformGroup tg = new TransformGroup();
482            Transform3D trans = new Transform3D();
483            if ( translation != null ) {
484                trans.setTranslation( new Vector3d( translation ) );
485                tg.setTransform( trans );
486            }
487            tg.setCapability( BranchGroup.ALLOW_DETACH );
488            tg.setCapability( Group.ALLOW_CHILDREN_READ );
489            tg.addChild( new ColorCube( 0.2f ) );
490            BranchGroup b = new BranchGroup();
491            b.setCapability( BranchGroup.ALLOW_DETACH );
492            b.addChild( tg );
493    
494            tg.addChild( new Sphere( 0.3f, app ) );
495    
496            return b;
497        }
498    
499        private JPanel createButtons() {
500            JPanel buttonPanel = new JPanel( new GridBagLayout() );
501            GridBagConstraints gb = new GridBagConstraints();
502            gb.gridx = 0;
503            gb.gridy = 0;
504    
505            JRadioButton help = new JRadioButton( "Activate help" );
506            help.addActionListener( new ActionListener() {
507                public void actionPerformed( ActionEvent e ) {
508                    if ( helpLister.isVisible() ) {
509                        helpLister.setVisible( false );
510                        ( (JRadioButton) e.getSource() ).setText( "Activate help" );
511                    } else {
512                        helpLister.setVisible( true );
513                        ( (JRadioButton) e.getSource() ).setText( "De-Activate help" );
514    
515                    }
516                }
517            } );
518            buttonPanel.add( help, gb );
519    
520            gb.insets = new Insets( 10, 10, 10, 10 );
521            gb.gridx++;
522            JButton button = new JButton( "Open File" );
523            button.setMnemonic( KeyEvent.VK_O );
524            button.addActionListener( this );
525            buttonPanel.add( button, gb );
526    
527            gb.gridx++;
528            button = new JButton( "Export File" );
529            button.setMnemonic( KeyEvent.VK_O );
530            button.addActionListener( this );
531            buttonPanel.add( button, gb );
532    
533            return buttonPanel;
534        }
535    
536        private void readFile( String fileName ) {
537    
538            if ( fileName == null || "".equals( fileName.trim() ) ) {
539                throw new InvalidParameterException( "the file name may not be null or empty" );
540            }
541            fileName = fileName.trim();
542    
543            final Open3DFile openFile = new Open3DFile( fileName, this );
544    
545            final JDialog dialog = new JDialog( this, "Loading", true );
546    
547            dialog.getContentPane().setLayout( new BorderLayout() );
548            dialog.getContentPane().add(
549                                         new JLabel( "<HTML>Loading file:<br>" + fileName + "<br>Please wait!</HTML>",
550                                                     SwingConstants.CENTER ), BorderLayout.NORTH );
551            final JProgressBar progressBar = new JProgressBar();
552            progressBar.setStringPainted( true );
553            progressBar.setIndeterminate( false );
554            dialog.getContentPane().add( progressBar, BorderLayout.CENTER );
555    
556            dialog.pack();
557            dialog.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE );
558            dialog.setResizable( false );
559            dialog.setLocationRelativeTo( this );
560    
561            final Thread openThread = new Thread() {
562                /**
563                 * Opens the file in a separate thread.
564                 */
565                @Override
566                public void run() {
567                    openFile.openFile( progressBar );
568                    if ( dialog.isDisplayable() ) {
569                        dialog.setVisible( false );
570                        dialog.dispose();
571                    }
572                }
573            };
574            openThread.start();
575    
576            dialog.setVisible( true );
577            BranchGroup result = openFile.getOpenedFile();
578            //
579            if ( result != null ) {
580                addBranchGroupToScene( result );
581                File f = new File( fileName );
582                setTitle( WIN_TITLE + f.getName() );
583            } else {
584                showExceptionDialog( "The file: " + fileName
585                                     + " could not be read,\nSee error log for detailed information." );
586            }
587    
588        }
589    
590        /**
591         * Shows an export dialog to the user.
592         */
593        @SuppressWarnings("unchecked")
594        private void doExport() {
595    
596            Export3DFile exportEvaluater = new Export3DFile( this );
597            // find the scene graph to export
598            Enumeration<Node> en = rotationGroup.getAllChildren();
599            BranchGroup toExport = null;
600            if ( en.hasMoreElements() ) {
601                toExport = (BranchGroup) en.nextElement();
602            }
603            if ( toExport == null ) {
604                showExceptionDialog( "Could not get the scene to export." );
605                return;
606            }
607            StringBuilder sb = exportEvaluater.exportBranchgroup( toExport );
608            if ( sb.length() == 0 ) {
609                showExceptionDialog( "Exporting failed, see error log for details." );
610                return;
611            }
612            addBranchGroupToScene( toExport );
613            JFileChooser chooser = createFileChooser( null );
614            int result = chooser.showSaveDialog( this );
615            if ( JFileChooser.APPROVE_OPTION == result ) {
616                File f = chooser.getSelectedFile();
617                FileFilter ff = chooser.getFileFilter();
618                if ( ff instanceof CustomFileFilter ) {
619                    prefs.put( LAST_EXTENSION, ( (CustomFileFilter) ff ).getExtension( f ) );
620                    prefs.put( OPEN_KEY, f.getParent() );
621                }
622                try {
623                    FileWriter output = new FileWriter( f );
624                    output.write( sb.toString() );
625                    output.flush();
626                    output.close();
627                } catch ( IOException e ) {
628                    LOG.logError( e.getMessage(), e );
629                    showExceptionDialog( "Exporting failed, see error log for details." );
630                }
631    
632            }
633        }
634    
635        /**
636         * @param errorMessage
637         *            to display
638         */
639        public void showExceptionDialog( String errorMessage ) {
640            JOptionPane.showMessageDialog( this, errorMessage );
641        }
642    
643        /*
644         * (non-Javadoc)
645         *
646         * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
647         */
648        public void actionPerformed( ActionEvent e ) {
649            Object source = e.getSource();
650            if ( source instanceof JButton ) {
651                JButton clicked = (JButton) source;
652                if ( clicked.getText().startsWith( "Export" ) ) {
653                    doExport();
654                } else {
655                    JFileChooser fileChooser = createFileChooser( supportedOpenFilter );
656                    int result = fileChooser.showOpenDialog( this );
657                    if ( JFileChooser.APPROVE_OPTION == result ) {
658                        File f = fileChooser.getSelectedFile();
659                        if ( f != null ) {
660                            String path = f.getAbsolutePath();
661                            prefs.put( LAST_EXTENSION, ( (CustomFileFilter) fileChooser.getFileFilter() ).getExtension( f ) );
662                            prefs.put( OPEN_KEY, f.getParent() );
663                            readFile( path );
664                        }
665    
666                    }
667                }
668            }
669        }
670    
671        /*
672         * (non-Javadoc)
673         *
674         * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
675         */
676        public void keyPressed( KeyEvent arg0 ) {
677            // nottin
678        }
679    
680        /*
681         * (non-Javadoc)
682         *
683         * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
684         */
685        public void keyReleased( KeyEvent arg0 ) {
686            // nottin
687        }
688    
689        /*
690         * (non-Javadoc)
691         *
692         * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
693         */
694        public void keyTyped( KeyEvent e ) {
695            double x = 0;
696            double y = 0;
697            double z = 0;
698            if ( e.getKeyChar() == 'q' ) {
699                System.exit( 0 );
700            } else if ( e.getKeyChar() == 'x' ) {
701                x = zoomX;
702            } else if ( e.getKeyChar() == 'X' ) {
703                x = -zoomX;
704            } else if ( e.getKeyChar() == 'y' ) {
705                y = zoomY;
706            } else if ( e.getKeyChar() == 'Y' ) {
707                y = -zoomY;
708            } else if ( e.getKeyChar() == 'z' ) {
709                z = zoomZ;
710            } else if ( e.getKeyChar() == 'Z' ) {
711                z = -zoomZ;
712            }
713    
714            TransformGroup viewToWorld = simpleUniverse.getViewingPlatform().getViewPlatformTransform();
715            Transform3D trans = new Transform3D();
716            viewToWorld.getTransform( trans );
717            trans.invert();
718            Vector3d translation = new Vector3d();
719            trans.get( translation );
720    
721            x += translation.x;
722            y += translation.y;
723            z += translation.z;
724    
725            Point3d eye = new Point3d( x, y, z );
726            trans.lookAt( eye, centroid, upVector );
727            LOG.logDebug( "Trans after:\n" + trans + "\ncentroid: " + centroid );
728            LOG.logDebug( "Center: " + centroid );
729            LOG.logDebug( "eye: " + eye );
730    
731            Vector3d dist = new Vector3d( centroid );
732            dist.sub( eye );
733            // trackBall.setSchedulingBounds( new BoundingSphere( centroid, dist.length() ) );
734            viewToWorld.setTransform( trans );
735    
736        }
737    
738        /**
739         * @param args
740         */
741        public static void main( String[] args ) {
742            View3DFile viewer = new View3DFile( true );
743            viewer.toFront();
744    
745        }
746    
747        /**
748         *
749         * The <code>CustomFileFilter</code> class adds functionality to the filefilter mechanism of the JFileChooser.
750         *
751         * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
752         *
753         * @author last edited by: $Author:$
754         *
755         * @version $Revision:$, $Date:$
756         *
757         */
758        private class CustomFileFilter extends FileFilter {
759    
760            private List<String> acceptedExtensions;
761    
762            private String desc;
763    
764            /**
765             * @param acceptedExtensions
766             *            list of extensions this filter accepts.
767             * @param description
768             *            to show
769             */
770            CustomFileFilter( List<String> acceptedExtensions, String description ) {
771                this.acceptedExtensions = new ArrayList<String>( acceptedExtensions.size() );
772                StringBuilder sb = new StringBuilder();
773                if ( acceptedExtensions.size() > 0 ) {
774    
775                    sb.append( "(" );
776                    int i = 0;
777                    for ( String ext : acceptedExtensions ) {
778                        if ( ext.startsWith( "." ) ) {
779                            ext = ext.substring( 1 );
780                        } else if ( ext.startsWith( "*." ) ) {
781                            ext = ext.substring( 2 );
782                        } else if ( ext.startsWith( "*" ) ) {
783                            ext = ext.substring( 1 );
784                        }
785    
786                        this.acceptedExtensions.add( ext.trim().toUpperCase() );
787                        sb.append( "*." );
788                        sb.append( ext );
789                        if ( ++i < acceptedExtensions.size() ) {
790                            sb.append( ", " );
791                        }
792                    }
793                    sb.append( ")" );
794                }
795                sb.append( description );
796                desc = sb.toString();
797            }
798    
799            /**
800             * @param extension
801             * @return true if the extension is accepted
802             */
803            public boolean accepts( String extension ) {
804                return extension != null && acceptedExtensions.contains( extension.toUpperCase() );
805            }
806    
807            @Override
808            public boolean accept( File pathname ) {
809                if ( pathname.isDirectory() ) {
810                    return true;
811                }
812    
813                String extension = getExtension( pathname );
814                if ( extension != null ) {
815                    if ( acceptedExtensions.contains( extension.trim().toUpperCase() ) ) {
816                        return true;
817                    }
818                }
819                return false;
820            }
821    
822            /**
823             * @param f
824             * @return the file extension (e.g. gml/shp/xml etc.)
825             */
826            String getExtension( File f ) {
827                String ext = null;
828                String s = f.getName();
829                int i = s.lastIndexOf( '.' );
830    
831                if ( i > 0 && i < s.length() - 1 ) {
832                    ext = s.substring( i + 1 ).toLowerCase();
833                }
834                return ext;
835            }
836    
837            @Override
838            public String getDescription() {
839                return desc;
840            }
841        }
842    
843    }