001    //$HeadURL: $
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 static java.lang.System.exit;
040    import static org.deegree.ogcbase.CommonNamespaces.CITYGMLNS;
041    import static org.deegree.ogcbase.CommonNamespaces.CITYGML_PREFIX;
042    import static org.deegree.ogcbase.CommonNamespaces.GMLNS;
043    import static org.deegree.ogcbase.CommonNamespaces.GML_PREFIX;
044    
045    import java.awt.image.BufferedImage;
046    import java.io.BufferedReader;
047    import java.io.BufferedWriter;
048    import java.io.File;
049    import java.io.FileNotFoundException;
050    import java.io.FileWriter;
051    import java.io.IOException;
052    import java.io.InputStreamReader;
053    import java.util.Calendar;
054    import java.util.Enumeration;
055    import java.util.GregorianCalendar;
056    import java.util.HashMap;
057    import java.util.Map;
058    
059    import javax.imageio.ImageIO;
060    import javax.media.j3d.Appearance;
061    import javax.media.j3d.Background;
062    import javax.media.j3d.BoundingBox;
063    import javax.media.j3d.Bounds;
064    import javax.media.j3d.BranchGroup;
065    import javax.media.j3d.Geometry;
066    import javax.media.j3d.GeometryArray;
067    import javax.media.j3d.Group;
068    import javax.media.j3d.ImageComponent;
069    import javax.media.j3d.ImageComponent2D;
070    import javax.media.j3d.Leaf;
071    import javax.media.j3d.Material;
072    import javax.media.j3d.Node;
073    import javax.media.j3d.Shape3D;
074    import javax.media.j3d.Texture;
075    import javax.media.j3d.Transform3D;
076    import javax.media.j3d.TransformGroup;
077    import javax.media.j3d.TriangleArray;
078    import javax.vecmath.AxisAngle4d;
079    import javax.vecmath.Color3f;
080    import javax.vecmath.Point3d;
081    import javax.vecmath.TexCoord2f;
082    import javax.vecmath.Vector3d;
083    
084    import org.deegree.framework.log.ILogger;
085    import org.deegree.framework.log.LoggerFactory;
086    import org.deegree.framework.xml.XMLFragment;
087    import org.deegree.framework.xml.XMLTools;
088    import org.deegree.ogcbase.CommonNamespaces;
089    import org.jdesktop.j3d.loaders.vrml97.VrmlLoader;
090    import org.w3c.dom.Document;
091    import org.w3c.dom.Element;
092    
093    import com.sun.j3d.loaders.IncorrectFormatException;
094    import com.sun.j3d.loaders.ParsingErrorException;
095    import com.sun.j3d.loaders.Scene;
096    import com.sun.j3d.utils.geometry.GeometryInfo;
097    
098    /**
099     * The <code>J3DToCityGMLExporter</code> exports a J3D scene to citygml level 1.
100     *
101     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
102     *
103     * @author last edited by: $Author:$
104     *
105     * @version $Revision:$, $Date:$
106     *
107     */
108    
109    public class J3DToCityGMLExporter implements J3DExporter {
110        static {
111            try {
112                new VrmlLoader();
113            } catch ( NoClassDefFoundError c ) {
114                libHelp( c );
115            }
116        }
117    
118        private static ILogger LOG = LoggerFactory.getLogger( J3DToCityGMLExporter.class );
119    
120        private final static String PRE_C = CITYGML_PREFIX + ":";
121    
122        private final static String PRE_G = GML_PREFIX + ":";
123    
124        private static int textureCount = 0;
125    
126        // Instance variables.
127        private String name = null;
128    
129        private String crsName = "EPSG:31466";
130    
131        private String cityGMLFunction = "1001";
132    
133        private String textureOutputDir;
134    
135        private boolean asWFSTransaction = false;
136    
137        // Will hold the texture file names if two different geometries use a single texture.
138        // private Map<String, String> savedTextures = new HashMap<String, String>( 200 );
139    
140        private static int numberOfSurfaces = 0;
141    
142        private Transform3D transformMatrix = null;
143    
144        private Transform3D rotationMatrix = null;
145    
146        private boolean inverseYZ = true;
147    
148        private HashMap<BufferedImage, String> cachedTextures = new HashMap<BufferedImage, String>();
149    
150        /**
151         * A default constructor allows the instantiation of this exported to supply the getParameterList method.
152         * <p>
153         * <b>NOTE</b> this constructor is only convenient, it should not be used carelessly. Instead use one of the other
154         * constructors for correct instantiation of this class.
155         * </p>
156         */
157        public J3DToCityGMLExporter() {
158            // nothing
159        }
160    
161        /**
162         * @param params
163         *            a hashmap to extract values from. For a list of supported parameters
164         * @throws IOException
165         *             if the key 'texdir' points to a file which is not a directory or is not writable
166         */
167        public J3DToCityGMLExporter( Map<String, String> params ) throws IOException {
168            String texDir = params.get( "texdir" );
169            if ( texDir == null || "".equals( texDir.trim() ) ) {
170                LOG.logInfo( "Setting tex dir to " + System.getProperty( "user.home" ) + "/j3dTextures/" );
171                texDir = System.getProperty( "user.home" ) + "/j3dTextures/";
172                File t = new File( texDir );
173                t.mkdir();
174            }
175            File tmp = new File( texDir );
176    
177            if ( !tmp.isDirectory() ) {
178                throw new IOException( "Given file: " + tmp.getAbsoluteFile()
179                                       + " exists, but is not a directory, please specify a valid directory." );
180            }
181            if ( !tmp.canWrite() ) {
182                throw new IOException( "Can not write to specified directory: " + tmp.getAbsoluteFile()
183                                       + ", please specify directory which can be written to." );
184            }
185            textureOutputDir = texDir;
186            if ( !textureOutputDir.endsWith( File.separator ) ) {
187                // just make sure the given path ends with the separator.
188                textureOutputDir += File.separator;
189            }
190    
191            double xTrans = 0;
192            String t = params.get( "xtranslation" );
193            if ( t != null && !"".equals( t ) ) {
194                try {
195                    xTrans = Double.parseDouble( t );
196                } catch ( NumberFormatException nfe ) {
197                    // nottin
198                }
199            }
200            double yTrans = 0;
201            t = params.get( "ytranslation" );
202            if ( t != null && !"".equals( t ) ) {
203                try {
204                    yTrans = Double.parseDouble( t );
205                } catch ( NumberFormatException nfe ) {
206                    // nottin
207                }
208            }
209            double zTrans = 0;
210            t = params.get( "ztranslation" );
211            if ( t != null && !"".equals( t ) ) {
212                try {
213                    zTrans = Double.parseDouble( t );
214                } catch ( NumberFormatException nfe ) {
215                    // nottin
216                }
217            }
218            AxisAngle4d rotAngle = null;
219            t = params.get( "rotationangle" );
220            if ( t != null && !"".equals( t ) ) {
221                try {
222                    String[] values = t.split( "," );
223                    LOG.logDebug( "Using rotation angle: " + t );
224                    if ( values.length != 4 ) {
225                        LOG.logError( "The rotation angle must have 4 values and must be separated by a ',' (without spaces) with following form x,y,z,rotation (in radians) . Ignoring your values." );
226                    } else {
227                        rotAngle = new AxisAngle4d( Double.parseDouble( values[0] ), Double.parseDouble( values[1] ),
228                                                    Double.parseDouble( values[2] ), Double.parseDouble( values[3] ) );
229                    }
230                } catch ( NumberFormatException nfe ) {
231                    // nottin
232                }
233            }
234            transformMatrix = new Transform3D();
235            transformMatrix.setTranslation( new Vector3d( xTrans, yTrans, zTrans ) );
236            if ( rotAngle != null ) {
237                rotationMatrix = new Transform3D();
238                rotationMatrix.setRotation( rotAngle );
239            }
240    
241            this.asWFSTransaction = false;
242            t = params.get( "wfstransaction" );
243            if ( t != null && !"".equals( t ) ) {
244                this.asWFSTransaction = t.equalsIgnoreCase( "y" ) || t.equalsIgnoreCase( "yes" );
245            }
246            this.name = "building";
247            t = params.get( "name" );
248            if ( t != null && !"".equals( t.trim() ) ) {
249                this.name = t;
250            }
251    
252            crsName = "EPSG:4326";
253            t = params.get( "srs" );
254            if ( t != null && !"".equals( t.trim() ) ) {
255                this.crsName = t;
256            }
257    
258            this.cityGMLFunction = "1001";
259            t = params.get( "buildingFunction" );
260            if ( t != null && !"".equals( t.trim() ) ) {
261                this.cityGMLFunction = t;
262            }
263            this.inverseYZ = false;
264            t = params.get( "inverseyz" );
265            if ( t != null && !"".equals( t.trim() ) ) {
266                this.inverseYZ = t.equalsIgnoreCase( "y" ) || t.equalsIgnoreCase( "yes" );
267            }
268        }
269    
270        /**
271         * @param citygmlName
272         *            of the exported branchgroup.
273         * @param crsName
274         *            of the crs to set the srsName of gmlNodes to, if <code>null</code> 'epsg:31466' will be used.
275         * @param cityGMLFunction
276         *            to insert om the gml
277         * @param textureOutputDirectory
278         *            the directory to output the textures to, if any.
279         * @param translationX
280         *            a value to add up to the found x - coordinates. If NaN, 0 will be used.
281         * @param translationY
282         *            a value to add up to the found y - coordinates. If NaN, 0 will be used.
283         * @param translationZ
284         *            a value to add up to the found z - coordinates. If NaN, 0 will be used.
285         * @param rotAngle
286         *            the rotation angle to which the all geometries should be rotated.
287         * @param asWFSTransaction
288         *            true if the scene should be exported as a wfs:Transaction document (the root node is wfs:Transaction)
289         *            or a cityGML document (the root node is citygml:Building).
290         * @param inverseYZ
291         * @throws IOException
292         *             if the something went wrong with wile creating, referring or addressing the given filePath.
293         */
294        public J3DToCityGMLExporter( String citygmlName, String crsName, String cityGMLFunction,
295                                     String textureOutputDirectory, double translationX, double translationY,
296                                     double translationZ, AxisAngle4d rotAngle, boolean asWFSTransaction, boolean inverseYZ )
297                                throws IOException {
298            if ( citygmlName != null && !"".equals( citygmlName.trim() ) ) {
299                this.name = citygmlName;
300            }
301    
302            if ( crsName != null && !"".equals( crsName.trim() ) ) {
303                this.crsName = crsName;
304            }
305    
306            if ( cityGMLFunction != null && !"".equals( cityGMLFunction.trim() ) ) {
307                this.cityGMLFunction = cityGMLFunction;
308            }
309    
310            if ( textureOutputDirectory != null && !"".equals( textureOutputDirectory.trim() ) ) {
311                File tmp = new File( textureOutputDirectory );
312                if ( !tmp.exists() ) {
313                    System.out.print( "The directory: " + textureOutputDirectory + " does not exist, create?: " );
314                    BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) );
315                    String answer = in.readLine();
316                    if ( answer != null && ( "yes".equalsIgnoreCase( answer ) || "y".equalsIgnoreCase( answer ) ) ) {
317                        if ( !tmp.mkdir() ) {
318                            throw new IOException( "Could not create given directory: " + tmp.getAbsoluteFile()
319                                                   + " please create it first!" );
320                        }
321                    } else {
322                        throw new IOException( "Given directory: " + tmp.getAbsoluteFile()
323                                               + " does not exist, please create it first!" );
324                    }
325                }
326                if ( !tmp.isDirectory() ) {
327                    throw new IOException( "Given file: " + tmp.getAbsoluteFile()
328                                           + " exists, but is not a directory, please specify a valid directory." );
329                }
330                if ( !tmp.canWrite() ) {
331                    throw new IOException( "Can not write to specified directory: " + tmp.getAbsoluteFile()
332                                           + ", please specify directory which can be written to." );
333                }
334                textureOutputDir = textureOutputDirectory;
335                if ( !textureOutputDirectory.endsWith( File.separator ) ) {
336                    // just make sure the given path ends with the separator.
337                    textureOutputDir += File.separator;
338                }
339    
340            } else {
341                throw new IOException( "No directory was given for the output textures." );
342            }
343            transformMatrix = new Transform3D();
344            if ( Double.isNaN( translationX ) ) {
345                translationX = 0;
346            }
347            if ( Double.isNaN( translationY ) ) {
348                translationY = 0;
349            }
350            if ( Double.isNaN( translationZ ) ) {
351                translationZ = 0;
352            }
353            transformMatrix.setTranslation( new Vector3d( translationX, translationY, translationZ ) );
354            LOG.logDebug( "Hier:\n" + transformMatrix + "\nrotanlge: " + rotAngle );
355            if ( rotAngle != null ) {
356                // transformMatrix.setRotation( rotAngle );
357                rotationMatrix = new Transform3D();
358                rotationMatrix.setRotation( rotAngle );
359            }
360            LOG.logDebug( "Hier2:\n" + transformMatrix );
361    
362            this.inverseYZ = inverseYZ;
363            this.asWFSTransaction = asWFSTransaction;
364        }
365    
366        public void export( StringBuilder result, Group j3dScene ) {
367            if ( j3dScene != null ) {
368                if ( j3dScene.getCapability( Group.ALLOW_CHILDREN_READ ) ) {
369                    Document doc = XMLTools.create();
370                    Element root = null;
371                    Element building = doc.createElementNS( CommonNamespaces.CITYGMLNS.toASCIIString(), PRE_C + "Building" );
372                    if ( asWFSTransaction ) {
373                        root = doc.createElementNS( CommonNamespaces.WFSNS.toASCIIString(), CommonNamespaces.WFS_PREFIX
374                                                                                            + ":Transaction" );
375                        root.setAttribute( "version", "1.1.0" );
376                        root.setAttribute( "service", "WFS" );
377                        Element insert = XMLTools.appendElement( root, CommonNamespaces.WFSNS, CommonNamespaces.WFS_PREFIX
378                                                                                               + ":Insert" );
379                        // Element featureCollection = XMLTools.appendElement( insert,
380                        // CommonNamespaces.WFSNS,
381                        // CommonNamespaces.WFS_PREFIX + ":FeatureCollection" );
382    
383                        // building = (Element)featureCollection.appendChild( building );
384                        building = (Element) insert.appendChild( building );
385    
386                    } else {
387                        root = doc.createElementNS( CITYGMLNS.toASCIIString(), PRE_C + "CityModel" );
388                        Element member = XMLTools.appendElement( root, CITYGMLNS, PRE_C + "cityObjectMember" );
389                        member.appendChild( building );
390                    }
391    
392                    // set the name element
393                    if ( name == null ) {
394                        name = j3dScene.getName();
395                    }
396                    XMLTools.appendElement( building, CommonNamespaces.GMLNS, PRE_G + "name", name );
397                    boolean calcBounds = false;
398                    Point3d lower = new Point3d( Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE );
399                    Point3d upper = new Point3d( Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE );
400                    if ( j3dScene.getCapability( Node.ALLOW_BOUNDS_READ ) ) {
401                        Bounds b = j3dScene.getBounds();
402                        if ( b != null ) {
403                            BoundingBox bBox = new BoundingBox( b );
404                            bBox.getLower( lower );
405                            bBox.getUpper( upper );
406                            // lower.x += transformMatrix.ttranslationX;
407                            // lower.y += translationY;
408                            transformMatrix.transform( lower );
409                            // upper.x += translationX;
410                            // upper.y += translationY;
411                            transformMatrix.transform( upper );
412                            Element boundBy = XMLTools.appendElement( building, GMLNS, PRE_G + "boundedBy" );
413                            Element env = XMLTools.appendElement( boundBy, GMLNS, PRE_G + "Envelope" );
414                            env.setAttribute( "srsName", crsName );
415                            Element lowerPos = XMLTools.appendElement( env, GMLNS, PRE_G + "pos", lower.x + " " + lower.y
416                                                                                                  + " " + lower.z );
417                            lowerPos.setAttribute( "srsDimension", "" + 3 );
418                            Element upperPos = XMLTools.appendElement( env, GMLNS, PRE_G + "pos", upper.x + " " + upper.y
419                                                                                                  + " " + upper.z );
420                            upperPos.setAttribute( "srsDimension", "" + 3 );
421                        }
422                    } else {
423                        LOG.logInfo( "The gml bounded by may not be read, it will be calculated from Hand." );
424                        calcBounds = true;
425                    }
426                    // citygml creation date
427                    GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance();
428                    String timeStamp = cal.get( Calendar.YEAR ) + "-" + ( cal.get( Calendar.MONTH ) + 1 ) + "-"
429                                       + cal.get( Calendar.DAY_OF_MONTH ) + "T" + cal.get( Calendar.HOUR_OF_DAY ) + ":"
430                                       + cal.get( Calendar.MINUTE );
431                    XMLTools.appendElement( building, CITYGMLNS, PRE_C + "creationDate", timeStamp );
432                    XMLTools.appendElement( building, CITYGMLNS, PRE_C + "function", this.cityGMLFunction );
433                    Element multiSurfaceLod2 = XMLTools.appendElement( building, CITYGMLNS, PRE_C + "lod2MultiSurface" );
434                    Element multiSurfaceParent = XMLTools.appendElement( multiSurfaceLod2, GMLNS, PRE_G + "MultiSurface" );
435                    multiSurfaceParent.setAttribute( "srsName", crsName );
436                    outputGroup( j3dScene, multiSurfaceParent, transformMatrix, calcBounds, lower, upper );
437                    root = (Element) doc.importNode( root, true );
438                    XMLFragment frag = new XMLFragment( root );
439                    result.append( frag.getAsPrettyString() );
440                    LOG.logDebug( "\n\nExported " + numberOfSurfaces + " surfaces." );
441                } else {
442                    LOG.logInfo( "The given branchgroup may not read it's children, nothing to export." );
443                }
444            } else {
445                LOG.logInfo( "The given branchgroup may not be null, nothing to export." );
446            }
447    
448        }
449    
450        /**
451         * Iterates over all children of the given branchgroup and appends their citygml representation to the rootNode.
452         *
453         * @param j3dScene
454         *            to export
455         * @param rootNode
456         *            to append to.
457         * @param calcBounds
458         *            if true the bounds should be calculated and placed in lower and upper.
459         * @param upper
460         *            bound
461         * @param lower
462         *            bound
463         */
464        @SuppressWarnings("unchecked")
465        private void outputGroup( Group j3dScene, Element rootNode, Transform3D transformation, boolean calcBounds,
466                                  Point3d lower, Point3d upper ) {
467            Enumeration<Node> en = j3dScene.getAllChildren();
468            LOG.logDebug( "Outputting a group." );
469            while ( en.hasMoreElements() ) {
470                Node n = en.nextElement();
471                if ( n != null ) {
472                    if ( n instanceof Group ) {
473                        LOG.logDebug( "A group: " + n );
474                        Transform3D tmpTrans = null;
475                        if ( n instanceof TransformGroup ) {
476                            TransformGroup tmp = (TransformGroup) n;
477                            tmpTrans = new Transform3D();
478                            tmp.getTransform( tmpTrans );
479                            if ( tmpTrans.getBestType() != Transform3D.IDENTITY ) {
480                                if ( inverseYZ ) {
481                                    Vector3d trans = new Vector3d();
482                                    tmpTrans.get( trans );
483                                    double tmpY = trans.y;
484                                    trans.y = -trans.z;
485                                    trans.z = tmpY;
486                                    tmpTrans.set( trans );
487                                }
488                                LOG.logDebug( "A transform group with transform:\n" + tmpTrans );
489                                if ( transformation != null ) {
490                                    transformation.mul( tmpTrans );
491                                }
492                                LOG.logDebug( "Resulting transform:\n" + transformation );
493                            }
494                        }
495                        outputGroup( (Group) n, rootNode, transformation, calcBounds, lower, upper );
496                        if ( tmpTrans != null && tmpTrans.getBestType() != Transform3D.IDENTITY ) {
497                            transformation.mulInverse( tmpTrans );
498                            LOG.logDebug( "After undoing the inverse of transformGroup transform:\n" + transformation );
499                        }
500                    } else if ( n instanceof Leaf ) {
501                        outputLeaf( (Leaf) n, rootNode, transformation, calcBounds, lower, upper );
502                    }
503                }
504            }
505    
506        }
507    
508        /**
509         * Checks if the given leaf is a shape3D or background leaf, and if so, their cityGML representation will be
510         * appended to the parent node.
511         *
512         * @param l
513         *            a j3d leaf.
514         * @param parent
515         *            to append to.
516         * @param transformation
517         * @param calcBounds
518         *            if true the bounds should be calculated and placed in lower and upper.
519         * @param upper
520         *            bound
521         * @param lower
522         *            bound
523         */
524        private void outputLeaf( Leaf l, Element parent, Transform3D transformation, boolean calcBounds, Point3d lower,
525                                 Point3d upper ) {
526            if ( l != null ) {
527                if ( l instanceof Shape3D ) {
528                    outputShape3D( (Shape3D) l, parent, transformation, calcBounds, lower, upper );
529                } else if ( l instanceof Background ) {
530                    outputBackground( (Background) l, parent );
531                } else {
532                    LOG.logInfo( "Don't know howto output object of instance: " + l.getClass().getName() );
533                }
534            }
535        }
536    
537        /**
538         * Appends the citygml representation to the given parent node.
539         *
540         * @param shape
541         *            (of the j3dScene) to append
542         * @param parent
543         *            to append to.
544         * @param transformation
545         * @param calcBounds
546         *            if true the bounds should be calculated and placed in lower and upper.
547         * @param upper
548         *            bound
549         * @param lower
550         *            bound
551         */
552        @SuppressWarnings("unchecked")
553        private void outputShape3D( Shape3D shape, Element parent, Transform3D transformation, boolean calcBounds,
554                                    Point3d lower, Point3d upper ) {
555            Enumeration<Geometry> geoms = shape.getAllGeometries();
556            String shapeName = shape.getName();
557            LOG.logDebug( "Outputting a Shape3d with name: " + shapeName );
558            Appearance app = shape.getAppearance();
559            float ambientIntensity = 0.5f;
560            Color3f ambient = new Color3f( 0.8f, 0.8f, 0.8f );
561            Color3f diffuse = new Color3f( 0.8f, 0.8f, 0.8f );
562            Color3f specular = new Color3f( 0.8f, 0.8f, 0.8f );
563    
564            if ( app != null ) {
565                Material mat = app.getMaterial();
566                if ( mat != null ) {
567                    mat.getAmbientColor( ambient );
568                    mat.getDiffuseColor( diffuse );
569                    mat.getSpecularColor( specular );
570                }
571            } else {
572                LOG.logInfo( "Shape3D has no material!, setting to gray!" );
573            }
574            while ( geoms.hasMoreElements() ) {
575                Geometry geom = geoms.nextElement();
576                if ( geom != null ) {
577                    if ( geom instanceof GeometryArray ) {
578                        GeometryArray ga = (GeometryArray) geom;
579                        if ( !( ga instanceof TriangleArray ) ) {
580                            LOG.logDebug( "Not a triangle Array geometry -> convert to triangles, original type is: " + ga );
581                            try {
582                                GeometryInfo inf = new GeometryInfo( ga );
583                                inf.convertToIndexedTriangles();
584                                ga = inf.getGeometryArray();
585                                LOG.logDebug( "The converted type is: " + ga );
586                            } catch ( IllegalArgumentException e ) {
587                                LOG.logInfo( "Could not create a triangle array of the: " + ga
588                                              );
589                            }
590                        }
591    
592                        int vertexCount = ga.getVertexCount();
593                        if ( vertexCount == 0 ) {
594                            LOG.logError( "No coordinates found in the geometryArray, this may not be." );
595                        } else {
596                            LOG.logDebug( "Number of vertices in shape3d: " + vertexCount );
597    
598                            Element appearance = parent.getOwnerDocument().createElementNS( CITYGMLNS.toASCIIString(),
599                                                                                            PRE_C + "appearance" );
600                            Element simpleTexture = parent.getOwnerDocument().createElementNS( CITYGMLNS.toASCIIString(),
601                                                                                               PRE_C + "SimpleTexture" );
602    
603                            int vertexFormat = ga.getVertexFormat();
604    
605                            /**
606                             * Textures
607                             */
608                            TexCoord2f[] texCoords = null;
609                            if ( ( GeometryArray.TEXTURE_COORDINATE_2 & vertexFormat ) == GeometryArray.TEXTURE_COORDINATE_2 ) {
610                                LOG.logDebug( "The Geometry has a texture attached to it." );
611    
612                                texCoords = new TexCoord2f[vertexCount];
613                                for ( int i = 0; i < texCoords.length; ++i ) {
614                                    texCoords[i] = new TexCoord2f( 0, 0 );
615                                }
616                                ga.getTextureCoordinates( 0, ga.getInitialTexCoordIndex( 0 ), texCoords );
617                                StringBuilder texCoordsAsString = new StringBuilder( texCoords.length * 2 );
618                                for ( TexCoord2f coord : texCoords ) {
619                                    texCoordsAsString.append( coord.x ).append( " " ).append( coord.y ).append( " " );
620                                }
621    
622                                // TODO try to get all textures of all texture units
623                                if ( app != null ) {
624                                    Texture tex = app.getTexture();
625                                    if ( tex != null ) {
626                                        String texName = tex.getName();
627                                        LOG.logDebug( "Texture name: " + texName );
628                                        ImageComponent ic = tex.getImage( 0 );
629                                        if ( ic != null ) {
630                                            if ( ic instanceof ImageComponent2D ) {
631                                                LOG.logDebug( "ImageComponent name: " + ( (ImageComponent2D) ic ).getName() );
632                                                BufferedImage bi = ( (ImageComponent2D) ic ).getImage();
633                                                if ( bi != null ) {
634                                                    if ( !cachedTextures.containsKey( bi ) ) {
635                                                        if ( texName == null || "".equals( texName.trim() ) ) {
636                                                            texName = name + "_texture_" + textureCount++;
637                                                        }
638                                                        texName += ( ".jpg" );
639                                                        LOG.logDebug( "No cached texture found: " + texName );
640                                                        try {
641                                                            File f = new File( textureOutputDir, texName );
642                                                            ImageIO.write( bi, "jpg", f );
643                                                            LOG.logDebug( "Wrote texture to: " + f.getAbsolutePath() );
644                                                        } catch ( IOException e ) {
645                                                            LOG.logError( "Failed to write texture: " + texName, e );
646                                                        }
647                                                        cachedTextures.put( bi, texName );
648                                                    } else {
649                                                        LOG.logDebug( "Using old texture reference: " + texName );
650                                                        texName = cachedTextures.get( bi );
651                                                    }
652    
653                                                    // add the texture to the appearance node
654    
655                                                    XMLTools.appendElement( simpleTexture, CITYGMLNS, PRE_C + "textureMap",
656                                                                            textureOutputDir + texName );
657                                                    XMLTools.appendElement( simpleTexture, CITYGMLNS,
658                                                                            PRE_C + "textureType", "specific" );
659                                                    XMLTools.appendElement( simpleTexture, CITYGMLNS, PRE_C + "repeat", "0" );
660                                                }
661                                            }
662                                        }
663                                    }
664                                }
665                            } else {
666                                Element material = XMLTools.appendElement( appearance, CITYGMLNS, PRE_C + "Material" );
667                                XMLTools.appendElement( material, CITYGMLNS, PRE_C + "ambientIntensity", ""
668                                                                                                         + ambientIntensity );
669                                // although the citgml spec does not contain it, we set the ambient color :(
670                                XMLTools.appendElement( material, CITYGMLNS, PRE_C + "ambientColor", ambient.x + " "
671                                                                                                     + ambient.y + " "
672                                                                                                     + ambient.z );
673                                XMLTools.appendElement( material, CITYGMLNS, PRE_C + "specularColor", specular.x + " "
674                                                                                                      + specular.y + " "
675                                                                                                      + specular.z );
676                                XMLTools.appendElement( material, CITYGMLNS, PRE_C + "diffuseColor", diffuse.x + " "
677                                                                                                     + diffuse.y + " "
678                                                                                                     + diffuse.z );
679                            }
680    
681                            Point3d[] coords = new Point3d[vertexCount];
682                            for ( int i = 0; i < coords.length; ++i ) {
683                                coords[i] = new Point3d( 0, 0, 0 );
684                            }
685                            ga.getCoordinates( ga.getInitialVertexIndex(), coords );
686                            LOG.logDebug( "Number of coords in geometry: " + coords.length );
687    
688                            if ( coords.length > 0 && coords[0] != null ) {
689    
690                                if ( ga instanceof TriangleArray ) {
691                                    LOG.logInfo( "Using triangles for the coords." );
692                                    int i = 0;
693                                    for ( ; i < coords.length; i += 3 ) {
694                                        Element surfaceMember = XMLTools.appendElement( parent, GMLNS, PRE_G
695                                                                                                       + "surfaceMember" );
696    
697                                        Element texturedSurface = XMLTools.appendElement( surfaceMember, CITYGMLNS,
698                                                                                          PRE_C + "TexturedSurface" );
699                                        texturedSurface.setAttribute( "orientation", "+" );
700    
701                                        Element baseSurface = XMLTools.appendElement( texturedSurface, GMLNS,
702                                                                                      PRE_G + "baseSurface" );
703                                        Element polygon = XMLTools.appendElement( baseSurface, GMLNS, PRE_G + "Polygon" );
704                                        Element exterior = XMLTools.appendElement( polygon, GMLNS, PRE_G + "exterior" );
705                                        Element lRing = XMLTools.appendElement( exterior, GMLNS, PRE_G + "LinearRing" );
706                                        if ( ( i + 3 ) <= coords.length ) {
707                                            for ( int j = 0; j < 3; j++ ) {
708                                                Point3d coord = coords[i + j];
709                                                // System.out.println( "Untranslated point: " + coord );
710                                                // transformMatrix.transform( coord );
711                                                // first do a transform on the old coords,
712                                                if ( inverseYZ ) {
713                                                    double coordY = coord.y;
714                                                    coord.y = -coord.z;
715                                                    coord.z = coordY;
716                                                }
717                                                if ( rotationMatrix != null ) {
718                                                    rotationMatrix.transform( coord );
719                                                }
720                                                // than translate
721                                                transformation.transform( coord );
722    
723                                                // use point3d.equals method to see if already present.
724                                                // if ( resultingPoints.contains( coord ) ) {
725                                                // resultingPoints.add( coord );
726                                                // check the bounds if set
727                                                if ( calcBounds ) {
728                                                    if ( coord.x < lower.x ) {
729                                                        lower.x = coord.x;
730                                                    }
731                                                    if ( coord.y < lower.y ) {
732                                                        lower.y = coord.y;
733                                                    }
734                                                    if ( coord.z < lower.z ) {
735                                                        lower.z = coord.z;
736                                                    }
737                                                    if ( coord.x > upper.x ) {
738                                                        upper.x = coord.x;
739                                                    }
740                                                    if ( coord.y > upper.y ) {
741                                                        upper.y = coord.y;
742                                                    }
743                                                    if ( coord.z > upper.z ) {
744                                                        upper.z = coord.z;
745                                                    }
746                                                }
747                                                XMLTools.appendElement( lRing, GMLNS, PRE_G + "pos", coord.x + " "
748                                                                                                     + coord.y + " "
749                                                                                                     + coord.z );
750    
751                                            }
752                                            Point3d tmp = coords[i];
753                                            XMLTools.appendElement( lRing, GMLNS, PRE_G + "pos", tmp.x + " " + tmp.y + " "
754                                                                                                 + tmp.z );
755                                        } else {// no three points are left;
756                                            LOG.logInfo( "One of the geometries have an inconsistent number of points ("
757                                                         + ( coords.length - i ) + ")to create triangles-> correcting!" );
758                                            Point3d current = coords[i];
759                                            // transformMatrix.transform( current );
760                                            transformation.transform( current );
761                                            Point3d previous = coords[i - 1];
762                                            // transformMatrix.transform( previous );
763                                            transformation.transform( previous );
764                                            Point3d tmp = null;
765                                            switch ( coords.length - i ) {
766                                            case 1: // take last two nodes and create a triangle.
767                                                tmp = coords[i - 2];
768                                                break;
769                                            default: // take last node and create a triangle.
770                                                tmp = coords[i + 1];
771                                                break;
772                                            }
773                                            // transformMatrix.transform( tmp );
774                                            transformation.transform( tmp );
775                                            XMLTools.appendElement( lRing, GMLNS, PRE_G + "pos", current.x + " "
776                                                                                                 + current.y + " "
777                                                                                                 + current.z );
778                                            XMLTools.appendElement( lRing, GMLNS, PRE_G + "pos", previous.x + " "
779                                                                                                 + previous.y + " "
780                                                                                                 + previous.z );
781                                            XMLTools.appendElement( lRing, GMLNS, PRE_G + "pos", tmp.x + " " + tmp.y + " "
782                                                                                                 + tmp.z );
783                                            XMLTools.appendElement( lRing, GMLNS, PRE_G + "pos", current.x + " "
784                                                                                                 + current.y + " "
785                                                                                                 + current.z );
786    
787                                        }
788    
789                                        // add the appearance
790                                        Element tmp = (Element) appearance.cloneNode( true );
791                                        if ( texCoords != null && ( i + 2 ) < texCoords.length ) {
792                                            Element sTex = (Element) simpleTexture.cloneNode( true );
793                                            XMLTools.appendElement( sTex, CITYGMLNS, PRE_C + "textureCoordinates",
794                                                                    texCoords[i].x + " " + texCoords[i].y + " "
795                                                                                            + texCoords[i + 1].x + " "
796                                                                                            + texCoords[i + 1].y + " "
797                                                                                            + texCoords[i + 2].x + " "
798                                                                                            + texCoords[i + 2].y + " "
799                                                                                            + texCoords[i].x + " "
800                                                                                            + texCoords[i].y );
801                                            tmp.appendChild( sTex );
802                                        }
803                                        texturedSurface.appendChild( tmp );
804                                        numberOfSurfaces++;
805                                    }
806                                } else { // try to set as polygon.
807                                    LOG.logInfo( "Setting points as polygon." );
808                                    Point3d firstCoord = coords[0];
809                                    // transformMatrix.transform( firstCoord );
810                                    transformation.transform( firstCoord );
811                                    Point3d lastCoord = null;
812                                    Element texturedSurface = XMLTools.appendElement( parent, CITYGMLNS,
813                                                                                      PRE_C + "TexturedSurface" );
814                                    texturedSurface.setAttribute( "orientation", "+" );
815    
816                                    Element baseSurface = XMLTools.appendElement( texturedSurface, GMLNS, PRE_G
817                                                                                                          + "baseSurface" );
818                                    Element polygon = XMLTools.appendElement( baseSurface, GMLNS, PRE_G + "Polygon" );
819                                    Element exterior = XMLTools.appendElement( polygon, GMLNS, PRE_G + "exterior" );
820                                    Element lRing = XMLTools.appendElement( exterior, GMLNS, PRE_G + "LinearRing" );
821    
822                                    for ( int i = 0; i < coords.length; ++i ) {
823                                        Point3d coord = coords[i];
824                                        // transformMatrix.transform( coord );
825                                        transformation.transform( coord );
826                                        if ( calcBounds ) {
827                                            if ( coord.x < lower.x ) {
828                                                lower.x = coord.x;
829                                            }
830                                            if ( coord.y < lower.y ) {
831                                                lower.y = coord.y;
832                                            }
833                                            if ( coord.z < lower.z ) {
834                                                lower.z = coord.z;
835                                            }
836                                            if ( coord.x > upper.x ) {
837                                                upper.x = coord.x;
838                                            }
839                                            if ( coord.y > upper.y ) {
840                                                upper.y = coord.y;
841                                            }
842                                            if ( coord.z > upper.z ) {
843                                                upper.z = coord.z;
844                                            }
845                                        }
846                                        XMLTools.appendElement( lRing, GMLNS, PRE_G + "pos", coord.x + " " + coord.y + " "
847                                                                                             + coord.z );
848                                        lastCoord = coord;// coords[i];
849                                    }
850                                    if ( !firstCoord.equals( lastCoord ) ) {
851                                        XMLTools.appendElement( lRing, GMLNS, PRE_G + "pos", firstCoord.x + " "
852                                                                                             + firstCoord.y + " "
853                                                                                             + firstCoord.z );
854                                    }
855    
856                                    // add the appearance
857                                    Element tmp = (Element) appearance.cloneNode( true );
858                                    texturedSurface.appendChild( tmp );
859                                }
860    
861                            }
862    
863                            /**
864                             * NORMALS
865                             */
866                            if ( ( GeometryArray.NORMALS & vertexFormat ) == GeometryArray.NORMALS ) {
867                                LOG.logDebug( "The Geometry has normals, but they are not exported (yet)." );
868                                // Vector3f[] normals = new Vector3f[vertexCount];
869                                // for ( int i = 0; i < normals.length; ++i ) {
870                                // normals[i] = new Vector3f( 0, 0, 0 );
871                                // }
872                                // ga.getNormals( 0, normals );
873                                // for ( Vector3f normal : normals ) {
874                                // parent.append( normal ).append( " " );
875                                // }
876                            }
877                            /**
878                             * Colors 3f
879                             */
880                            if ( ( GeometryArray.COLOR_3 & vertexFormat ) == GeometryArray.COLOR_3 ) {
881                                LOG.logDebug( "The Geometry has Colors per Vertex with 3 chanels, but they are not exported (yet)." );
882                                // Color3f[] colors = new Color3f[vertexCount];
883                                // for ( int i = 0; i < colors.length; ++i ) {
884                                // colors[i] = new Color3f( 0, 0, 0 );
885                                // }
886                                // ga.getColors( 0, colors );
887                                // parent.append( "\n - Colors3f (" ).append( numberOfGeom ).append( "):
888                                // " );
889                                // for ( Color3f color : colors ) {
890                                // parent.append( color ).append( " " );
891                                // }
892                            }
893    
894                            /**
895                             * Colors 4f
896                             */
897                            if ( ( GeometryArray.COLOR_4 & vertexFormat ) == GeometryArray.COLOR_4 ) {
898                                LOG.logDebug( "The Geometry has Colors per Vertex with 4 chanels, but they are not exported (yet)." );
899                                // Color4f[] colors = new Color4f[vertexCount];
900                                // for ( int i = 0; i < colors.length; ++i ) {
901                                // colors[i] = new Color4f( 0, 0, 0, 0 );
902                                // }
903                                // ga.getColors( 0, colors );
904                                // parent.append( "\n - Colors4f (" ).append( numberOfGeom ).append( "):
905                                // " );
906                                // for ( Color4f color : colors ) {
907                                // parent.append( color ).append( " " );
908                                // }
909                            }
910    
911                            // Element appearance = XMLTools.appendElement( texturedSurface,
912                            // CITYGMLNS,
913                            // PRE_C + "appearance" );
914                        }
915                    } else {
916                        LOG.logInfo( "Only Shape3d Objects with geometry rasters are supported for outputing" );
917                    }
918                }
919            }
920    
921        }
922    
923        /**
924         * Don't know how to export the background yet.
925         *
926         * @param b
927         *            leaf of the j3dScene
928         * @param parent
929         *            to append to.
930         */
931        private void outputBackground( @SuppressWarnings("unused")
932        Background b, @SuppressWarnings("unused")
933        Element parent ) {
934            LOG.logError( "Cannot output a background yet." );
935        }
936    
937        public String getName() {
938            return "CityGML exporter";
939        }
940    
941        public String getShortDescription() {
942            return "Convert a given j3d:BranchGroup to CityGML LOD1";
943        }
944    
945        /**
946         * @param args
947         */
948        public static void main( String[] args ) {
949            if ( args.length == 0 ) {
950                outputHelp();
951            }
952            Map<String, String> params = new HashMap<String, String>( 5 );
953            for ( int i = 0; i < args.length; i++ ) {
954                String arg = args[i];
955                if ( arg != null && !"".equals( arg.trim() ) ) {
956                    arg = arg.trim();
957                    if ( arg.startsWith( "-" ) ) {
958                        arg = arg.substring( 1 );
959                    }
960                    if ( arg.equalsIgnoreCase( "?" ) || arg.equalsIgnoreCase( "h" ) ) {
961                        outputHelp();
962                    } else {
963                        if ( i + 1 < args.length ) {
964                            String val = args[++i];
965                            if ( val != null && !"".equals( val.trim() ) ) {
966                                params.put( arg.toLowerCase(), val.trim() );
967                            } else {
968                                LOG.logError( "Invalid value for parameter: " + arg );
969                            }
970                        } else {
971                            LOG.logError( "No value for parameter: " + arg );
972                        }
973                    }
974                }
975            }
976    
977            String fileName = params.get( "infile" );
978            if ( fileName == null || "".equals( fileName.trim() ) ) {
979                System.out.println( "\n\n" );
980                LOG.logError( "The -inFile parameter must be defined." );
981                System.out.println( "\n\n" );
982                outputHelp();
983            }
984            File tmpFile = new File( fileName );
985            if ( !tmpFile.exists() ) {
986                System.out.println( "\n\n" );
987                LOG.logError( "The file '" + fileName + "' given by the -inFile parameter does not exist." );
988                System.out.println( "\n\n" );
989                System.exit( 1 );
990            }
991            LOG.logInfo( "\n\nTrying to load scene, if you see no message beneath this message, please check your vrml file: "
992                         + fileName
993                         + "if the refrerred textures are java qouted.\nIf using a Windows os, the quoted directory separator (\\\\) should be used instead of a single one (\\)" );
994            VrmlLoader loader = new VrmlLoader();
995            try {
996                J3DToCityGMLExporter converter = new J3DToCityGMLExporter( params );
997                Scene scene = loader.load( fileName );
998                if ( scene != null ) {
999                    LOG.logInfo( "Successfully loaded the scene, trying to retrieve the branchgroup." );
1000                    BranchGroup bg = scene.getSceneGroup();
1001                    if ( bg != null && bg.numChildren() > 0 ) {
1002                        LOG.logInfo( "Retrieval of the j3d.Branchgroup was successful." );
1003                        StringBuilder output = new StringBuilder( 20000 );
1004                        converter.export( output, bg );
1005                        String outFile = params.get( "outfile" );
1006                        if ( outFile == null || "".equals( outFile.trim() ) ) {
1007                            System.out.println( output.toString() );
1008                        } else {
1009                            BufferedWriter fWriter = new BufferedWriter( new FileWriter( new File( outFile ) ) );
1010                            fWriter.write( output.toString() );
1011                            fWriter.flush();
1012                            fWriter.close();
1013                        }
1014                    } else {
1015                        System.out.println( "The branchgroup is null or has no childres, which means that the scene was not loaded correctly (did you correctly quoted the texture paths in your vrml-file: "
1016                                            + fileName + ")" );
1017                    }
1018                } else {
1019                    System.out.println( "The scene was not loaded (did you correctly quoted the texture paths in your vrml-file: "
1020                                        + fileName + ")" );
1021                }
1022            } catch ( NoClassDefFoundError c ) {
1023                libHelp( c );
1024            } catch ( FileNotFoundException e ) {
1025                LOG.logError( "Could not load vrm-file because: " + e.getMessage(), e );
1026            } catch ( IncorrectFormatException e ) {
1027                LOG.logError( "Could not load vrm-file because: " + e.getMessage(), e );
1028            } catch ( ParsingErrorException e ) {
1029                LOG.logError( "Could not load vrm-file because: " + e.getMessage(), e );
1030            } catch ( IOException e ) {
1031                LOG.logError( "Could not create converter because: " + e.getMessage(), e );
1032            } catch ( Exception e ) {
1033                LOG.logError( "Error while loading the file: " + fileName + " because: " + e.getMessage(), e );
1034            } catch ( Throwable t ) {
1035                LOG.logError( "Error while loading the file: " + fileName + " because: " + t.getMessage(), t );
1036            }
1037        }
1038    
1039        private static void outputHelp() {
1040            StringBuilder sb = new StringBuilder();
1041            sb.append( "The J3DToCityGMLExported program can be used to export a j3d branchgroup created from a vrml-file to citygml. \n" );
1042            sb.append( "Following parameters are supported:\n" );
1043            sb.append( "-inFile the /path/to/vrml-file\n" );
1044            sb.append( "-texDir directory to output the found textures to.\n" );
1045            sb.append( "[-outFile] the /path/to/the/output/file or standard output if not supplied.\n" );
1046            sb.append( "[-wfsTransaction y/n] output the citygml as part of a wfs-insert request (if omitted the citygml-building element will be root element).\n" );
1047            sb.append( "[-buildingFunction] the function this citygml building will have (if omitted 1001 will be used).\n" );
1048            sb.append( "[-srs] the coordinateSystem used for the citygml (if omitted 'EPSG:31466' will be used).\n" );
1049            sb.append( "[-xTranslation] the amount to add up to the x part of the found coordinates(if omitted '0' will be used).\n" );
1050            sb.append( "[-yTranslation] the amount to add up to the y part of the found coordinates(if omitted '0' will be used).\n" );
1051            sb.append( "[-zTranslation] the amount to add up to the z part of the found coordinates(if omitted '0' will be used).\n" );
1052            sb.append( "[-rotationAngle] the rotation angle given by comma seperated values as x,y,z,rotation(in radians)(if omitted no rotation will be applied).\n" );
1053            sb.append( "[-inverseYZ] (y/n)signal that the y and z coordinates should be inversed.\n" );
1054            sb.append( "-?|-h output this text\n" );
1055            sb.append( "example usage (converting the cool.vrml to a wfs:Transaction in 'epsg:12345' wit a xtranslation of 10000 and yTranslation of 50,\n" );
1056            sb.append( "outputing to cool_tower.xml in the directory my_output_dir (relative to the current dir)\n" );
1057            sb.append( "java -cp deegree.jar:lib/j3d/j3d-vrml97.jar:lib/log4j/log4j-1.2.9.jar:/lib/xml/jaxen-1.1-beta-8.jar org.deegree.tools.app3d.J3DToCityGMLExporter -inFile 'cool.vrml' -texDir 'my_output_dir' -outFile 'cool_tower.xml' -wfsTransaction 'y' -buildingFunction 'home office buildings' -xTranslation 10000 -yTranslation 50\n" );
1058            System.out.println( sb.toString() );
1059            System.exit( 1 );
1060        }
1061    
1062        private static void libHelp( NoClassDefFoundError c ) {
1063            StringBuilder sb = new StringBuilder( 800 );
1064            sb.append( "Could not load vrm-file because a specific class (" );
1065            sb.append( c.getMessage() );
1066            sb.append( ") was not found, the vrmlImporter uses following libraries: " );
1067            sb.append( "\n - $deegree-base$/lib/java3d/vecmath.jar" );
1068            sb.append( "\n - $deegree-base$/lib/java3d/j3dutils.jar" );
1069            sb.append( "\n - $deegree-base$/lib/java3d/j3dcore.jar" );
1070            sb.append( "\n - $deegree-base$/lib/j3d/j3d-vrml97.jar" );
1071            sb.append( "\n - $deegree-base$/lib/commons/commons-logging.jar" );
1072            sb.append( "\n - $deegree-base$/lib/log4j/log4j-1.2.9.jar" );
1073            sb.append( "\n - $deegree-base$/lib/xml/jaxen-1.1-beta-8.jar" );
1074            // LOG.logError( sb.toString() );
1075            System.out.println( sb.toString() );
1076            exit( 1 );
1077        }
1078    
1079        /*
1080         * (non-Javadoc)
1081         *
1082         * @see org.deegree.tools.app3d.J3DExporter#getParameterList()
1083         */
1084        public Map<String, String> getParameterMap() {
1085            Map<String, String> params = new HashMap<String, String>();
1086            params.put( "name", "The name of the building" );
1087            params.put( "texdir", "The directory to output the j3d objects' textures to." );
1088            params.put( "wfstransaction", "(yes/no) Output the citygml as part of a wfs-insert request." );
1089            params.put( "buildingfunction", "The function this citygml building will have." );
1090            params.put( "srs", "the coordinateSystem used." );
1091            params.put( "xtranslation", "The x translation." );
1092            params.put( "ytranslation", "The y translation." );
1093            params.put( "ztranslation", "The z translation." );
1094            params.put( "rotationangle", "Comma seperated values as x,y,z,rotation(in radians)." );
1095            params.put( "inverseyz", "(yes/no) Signal that the y and z coordinates should be swapped." );
1096            return params;
1097        }
1098    
1099    }