001 //$HeadURL: $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 This file is part of deegree. 004 Copyright (C) 2001-2008 by: 005 Department of Geography, University of Bonn 006 http://www.giub.uni-bonn.de/deegree/ 007 lat/lon GmbH 008 http://www.lat-lon.de 009 010 This library is free software; you can redistribute it and/or 011 modify it under the terms of the GNU Lesser General Public 012 License as published by the Free Software Foundation; either 013 version 2.1 of the License, or (at your option) any later version. 014 This library is distributed in the hope that it will be useful, 015 but WITHOUT ANY WARRANTY; without even the implied warranty of 016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 Lesser General Public License for more details. 018 You should have received a copy of the GNU Lesser General Public 019 License along with this library; if not, write to the Free Software 020 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 021 Contact: 022 023 Andreas Poth 024 lat/lon GmbH 025 Aennchenstr. 19 026 53177 Bonn 027 Germany 028 E-Mail: poth@lat-lon.de 029 030 Prof. Dr. Klaus Greve 031 Department of Geography 032 University of Bonn 033 Meckenheimer Allee 166 034 53115 Bonn 035 Germany 036 E-Mail: greve@giub.uni-bonn.de 037 ---------------------------------------------------------------------------*/ 038 039 package org.deegree.tools.app3d; 040 041 import static org.deegree.ogcbase.CommonNamespaces.CITYGMLNS; 042 import static org.deegree.ogcbase.CommonNamespaces.CITYGML_PREFIX; 043 import static org.deegree.ogcbase.CommonNamespaces.GMLNS; 044 import static org.deegree.ogcbase.CommonNamespaces.GML_PREFIX; 045 046 import java.awt.image.BufferedImage; 047 import java.io.BufferedReader; 048 import java.io.BufferedWriter; 049 import java.io.File; 050 import java.io.FileNotFoundException; 051 import java.io.FileWriter; 052 import java.io.IOException; 053 import java.io.InputStreamReader; 054 import java.util.Calendar; 055 import java.util.Enumeration; 056 import java.util.GregorianCalendar; 057 import java.util.HashMap; 058 import java.util.Map; 059 060 import javax.imageio.ImageIO; 061 import javax.media.j3d.Appearance; 062 import javax.media.j3d.Background; 063 import javax.media.j3d.BoundingBox; 064 import javax.media.j3d.Bounds; 065 import javax.media.j3d.BranchGroup; 066 import javax.media.j3d.Geometry; 067 import javax.media.j3d.GeometryArray; 068 import javax.media.j3d.Group; 069 import javax.media.j3d.ImageComponent; 070 import javax.media.j3d.ImageComponent2D; 071 import javax.media.j3d.Leaf; 072 import javax.media.j3d.Material; 073 import javax.media.j3d.Node; 074 import javax.media.j3d.Shape3D; 075 import javax.media.j3d.Texture; 076 import javax.media.j3d.TriangleArray; 077 import javax.vecmath.Color3f; 078 import javax.vecmath.Point3d; 079 import javax.vecmath.TexCoord2f; 080 081 import org.deegree.framework.log.ILogger; 082 import org.deegree.framework.log.LoggerFactory; 083 import org.deegree.framework.xml.XMLFragment; 084 import org.deegree.framework.xml.XMLTools; 085 import org.deegree.ogcbase.CommonNamespaces; 086 import org.jdesktop.j3d.loaders.vrml97.VrmlLoader; 087 import org.w3c.dom.Document; 088 import org.w3c.dom.Element; 089 090 import com.sun.j3d.loaders.IncorrectFormatException; 091 import com.sun.j3d.loaders.ParsingErrorException; 092 import com.sun.j3d.loaders.Scene; 093 import com.sun.j3d.utils.geometry.GeometryInfo; 094 095 /** 096 * The <code>J3DToCityGMLExporter</code> exports a J3D scene to citygml level 1. 097 * 098 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 099 * 100 * @author last edited by: $Author:$ 101 * 102 * @version $Revision:$, $Date:$ 103 * 104 */ 105 106 public class J3DToCityGMLExporter implements J3DExporter { 107 private static ILogger LOG = LoggerFactory.getLogger( J3DToCityGMLExporter.class ); 108 109 private final static String PRE_C = CITYGML_PREFIX + ":"; 110 111 private final static String PRE_G = GML_PREFIX + ":"; 112 113 private static int textureCount = 0; 114 115 // Instance variables. 116 private String name = null; 117 118 private String crsName = "EPSG:31466"; 119 120 private String cityGMLFunction = "1001"; 121 122 private String textureOutputDir; 123 124 private double translationX; 125 126 private double translationY; 127 128 private boolean asWFSTransaction = false; 129 130 // Will hold the texture file names if two different geometries use a single texture. 131 //private Map<String, String> savedTextures = new HashMap<String, String>( 200 ); 132 133 private double translationZ; 134 135 private static int numberOfSurfaces = 0; 136 137 /** 138 * @param citygmlName 139 * of the exported branchgroup. 140 * @param crsName 141 * of the crs to set the srsName of gmlNodes to, if <code>null</code> 'epsg:31466' will be used. 142 * @param cityGMLFunction 143 * to insert om the gml 144 * @param textureOutputDirectory 145 * the directory to output the textures to, if any. 146 * @param translationX 147 * a value to add up to the found x - coordinates. If NaN, 0 will be used. 148 * @param translationY 149 * a value to add up to the found y - coordinates. If NaN, 0 will be used. 150 * @param translationZ 151 * a value to add up to the found z - coordinates. If NaN, 0 will be used. 152 * @param asWFSTransaction 153 * true if the scene should be exported as a wfs:Transaction document (the root node is wfs:Transaction) 154 * or a cityGML document (the root node is citygml:Building). 155 * @throws IOException 156 * if the something went wrong with wile creating, referring or adressing the given filePath. 157 */ 158 public J3DToCityGMLExporter( String citygmlName, String crsName, String cityGMLFunction, 159 String textureOutputDirectory, double translationX, double translationY, 160 double translationZ, boolean asWFSTransaction ) throws IOException { 161 if ( citygmlName != null && !"".equals( citygmlName.trim() ) ) { 162 this.name = citygmlName; 163 } 164 165 if ( crsName != null && !"".equals( crsName.trim() ) ) { 166 this.crsName = crsName; 167 } 168 169 if ( cityGMLFunction != null && !"".equals( cityGMLFunction.trim() ) ) { 170 this.cityGMLFunction = cityGMLFunction; 171 } 172 173 if ( textureOutputDirectory != null && !"".equals( textureOutputDirectory.trim() ) ) { 174 File tmp = new File( textureOutputDirectory ); 175 if ( !tmp.exists() ) { 176 System.out.print( "The directory: " + textureOutputDirectory + " does not exist, create?: " ); 177 BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) ); 178 String answer = in.readLine(); 179 if ( answer != null && ( "yes".equalsIgnoreCase( answer ) || "y".equalsIgnoreCase( answer ) ) ) { 180 if ( !tmp.mkdir() ) { 181 throw new IOException( "Could not create given directory: " + tmp.getAbsoluteFile() 182 + " please create it first!" ); 183 } 184 } else { 185 throw new IOException( "Given directory: " + tmp.getAbsoluteFile() 186 + " does not exist, please create it first!" ); 187 } 188 } 189 if ( !tmp.isDirectory() ) { 190 throw new IOException( "Given file: " + tmp.getAbsoluteFile() 191 + " exists, but is not a directory, please specify a valid directory." ); 192 } 193 if ( !tmp.canWrite() ) { 194 throw new IOException( "Can not write to specified directory: " + tmp.getAbsoluteFile() 195 + ", please specify directory which can be written to." ); 196 } 197 textureOutputDir = textureOutputDirectory; 198 if ( !textureOutputDirectory.endsWith( File.separator ) ) { 199 // just make sure the given path ends with the separator. 200 textureOutputDir += File.separator; 201 } 202 203 } else { 204 throw new IOException( "No directory was given for the output textures." ); 205 } 206 207 if ( Double.isNaN( translationX ) ) { 208 translationX = 0; 209 } 210 if ( Double.isNaN( translationY ) ) { 211 translationY = 0; 212 } 213 if ( Double.isNaN( translationZ ) ) { 214 translationZ = 0; 215 } 216 this.translationX = translationX; 217 this.translationY = translationY; 218 this.translationZ = translationZ; 219 220 this.asWFSTransaction = asWFSTransaction; 221 } 222 223 public void export( StringBuilder result, Group j3dScene ) { 224 if ( j3dScene != null ) { 225 if ( j3dScene.getCapability( Group.ALLOW_CHILDREN_READ ) ) { 226 Document doc = XMLTools.create(); 227 Element root = null; 228 Element building = doc.createElementNS( CommonNamespaces.CITYGMLNS.toASCIIString(), PRE_C + "Building" ); 229 if ( asWFSTransaction ) { 230 root = doc.createElementNS( CommonNamespaces.WFSNS.toASCIIString(), 231 CommonNamespaces.WFS_PREFIX + ":Transaction" ); 232 root.setAttribute( "version", "1.1.0" ); 233 root.setAttribute( "service", "WFS" ); 234 Element insert = XMLTools.appendElement( root, 235 CommonNamespaces.WFSNS, 236 CommonNamespaces.WFS_PREFIX + ":Insert" ); 237 //Element featureCollection = XMLTools.appendElement( insert, CommonNamespaces.WFSNS, CommonNamespaces.WFS_PREFIX + ":FeatureCollection" ); 238 239 //building = (Element)featureCollection.appendChild( building ); 240 building = (Element)insert.appendChild( building ); 241 242 } else { 243 root = doc.createElementNS( CITYGMLNS.toASCIIString(), PRE_C + "CityModel" ); 244 Element member = XMLTools.appendElement( root, CITYGMLNS, PRE_C + "cityObjectMember" ); 245 member.appendChild( building ); 246 } 247 248 // set the name element 249 if ( name == null ) { 250 name = j3dScene.getName(); 251 } 252 XMLTools.appendElement( building, CommonNamespaces.GMLNS, PRE_G + "name", name ); 253 boolean calcBounds = false; 254 Point3d lower = new Point3d( Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE ); 255 Point3d upper = new Point3d( Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE ); 256 if ( j3dScene.getCapability( Node.ALLOW_BOUNDS_READ ) ) { 257 Bounds b = j3dScene.getBounds(); 258 if ( b != null ) { 259 BoundingBox bBox = new BoundingBox( b ); 260 bBox.getLower( lower ); 261 bBox.getUpper( upper ); 262 lower.x += translationX; 263 lower.y += translationY; 264 upper.x += translationX; 265 upper.y += translationY; 266 Element boundBy = XMLTools.appendElement( building, GMLNS, PRE_G + "boundedBy" ); 267 Element env = XMLTools.appendElement( boundBy, GMLNS, PRE_G + "Envelope" ); 268 env.setAttribute( "srsName", crsName ); 269 Element lowerPos = XMLTools.appendElement( env, GMLNS, PRE_G + "pos", lower.x + " " 270 + lower.y 271 + " " 272 + lower.z ); 273 lowerPos.setAttribute( "srsDimension", "" + 3 ); 274 Element upperPos = XMLTools.appendElement( env, GMLNS, PRE_G + "pos", upper.x + " " 275 + upper.y 276 + " " 277 + upper.z ); 278 upperPos.setAttribute( "srsDimension", "" + 3 ); 279 } 280 } else { 281 LOG.logInfo( "The gml bounded by may not be read, it will be calculated from Hand." ); 282 calcBounds = true; 283 } 284 // citygml creation date 285 GregorianCalendar cal = (GregorianCalendar) GregorianCalendar.getInstance(); 286 String timeStamp = cal.get( Calendar.YEAR ) + "-" 287 + ( cal.get( Calendar.MONTH ) + 1 ) 288 + "-" 289 + cal.get( Calendar.DAY_OF_MONTH ) 290 + "T" 291 + cal.get( Calendar.HOUR_OF_DAY ) 292 + ":" 293 + cal.get( Calendar.MINUTE ); 294 XMLTools.appendElement( building, CITYGMLNS, PRE_C + "creationDate", timeStamp ); 295 XMLTools.appendElement( building, CITYGMLNS, PRE_C + "function", this.cityGMLFunction ); 296 Element multiSurfaceLod2 = XMLTools.appendElement( building, CITYGMLNS, PRE_C + "lod2MultiSurface" ); 297 Element multiSurfaceParent = XMLTools.appendElement( multiSurfaceLod2, 298 GMLNS, 299 PRE_G + "MultiSurface" ); 300 multiSurfaceParent.setAttribute( "srsName", crsName ); 301 outputGroup( j3dScene, multiSurfaceParent, calcBounds, lower, upper ); 302 root = (Element) doc.importNode( root, true ); 303 XMLFragment frag = new XMLFragment( root ); 304 result.append( frag.getAsPrettyString() ); 305 LOG.logDebug( "\n\nExported " + numberOfSurfaces + " surfaces." ); 306 } else { 307 LOG.logInfo( "The given branchgroup may not read it's children, nothing to export." ); 308 } 309 } else { 310 LOG.logInfo( "The given branchgroup may not be null, nothing to export." ); 311 } 312 313 } 314 315 /** 316 * Iterates over all children of the given branchgroup and appends their citygml representation to the rootNode. 317 * 318 * @param j3dScene 319 * to export 320 * @param rootNode 321 * to append to. 322 * @param calcBounds 323 * if true the bounds should be calculated and placed in lower and upper. 324 * @param upper 325 * bound 326 * @param lower 327 * bound 328 */ 329 private void outputGroup( Group j3dScene, Element rootNode, boolean calcBounds, Point3d lower, Point3d upper ) { 330 Enumeration en = j3dScene.getAllChildren(); 331 LOG.logDebug( "Outputting a group." ); 332 while ( en.hasMoreElements() ) { 333 Node n = (Node) en.nextElement(); 334 if ( n != null ) { 335 if ( n instanceof Group ) { 336 outputGroup( (Group) n, rootNode, calcBounds, lower, upper ); 337 } else if ( n instanceof Leaf ) { 338 outputLeaf( (Leaf) n, rootNode, calcBounds, lower, upper ); 339 } 340 } 341 } 342 343 } 344 345 /** 346 * Checks if the given leaf is a shape3D or background leaf, and if so, their cityGML representation will be 347 * appended to the parent node. 348 * 349 * @param l 350 * a j3d leaf. 351 * @param parent 352 * to append to. 353 * @param calcBounds 354 * if true the bounds should be calculated and placed in lower and upper. 355 * @param upper 356 * bound 357 * @param lower 358 * bound 359 */ 360 private void outputLeaf( Leaf l, Element parent, boolean calcBounds, Point3d lower, Point3d upper ) { 361 if ( l != null ) { 362 if ( l instanceof Shape3D ) { 363 outputShape3D( (Shape3D) l, parent, calcBounds, lower, upper ); 364 } else if ( l instanceof Background ) { 365 outputBackground( (Background) l, parent ); 366 } else { 367 LOG.logInfo( "Don't know howto output object of instance: " + l.getClass().getName() ); 368 } 369 } 370 } 371 372 /** 373 * Appends the citygml representation to the given parent node. 374 * 375 * @param shape 376 * (of the j3dScene) to append 377 * @param parent 378 * to append to. 379 * @param calcBounds 380 * if true the bounds should be calculated and placed in lower and upper. 381 * @param upper 382 * bound 383 * @param lower 384 * bound 385 */ 386 private void outputShape3D( Shape3D shape, Element parent, boolean calcBounds, Point3d lower, Point3d upper ) { 387 Enumeration geoms = shape.getAllGeometries(); 388 String shapeName = shape.getName(); 389 LOG.logDebug( "Outputting a Shape3d with name: " + shapeName ); 390 Appearance app = shape.getAppearance(); 391 float ambientIntensity = 0.5f; 392 Color3f ambient = new Color3f( 0.8f, 0.8f, 0.8f ); 393 Color3f diffuse = new Color3f( 0.8f, 0.8f, 0.8f ); 394 Color3f specular = new Color3f( 0.8f, 0.8f, 0.8f ); 395 if ( app != null ) { 396 Material mat = app.getMaterial(); 397 if ( mat != null ) { 398 // ambientIntensity = mat.getShininess(); 399 mat.getAmbientColor( ambient ); 400 mat.getDiffuseColor( diffuse ); 401 mat.getSpecularColor( specular ); 402 } 403 } 404 while ( geoms.hasMoreElements() ) { 405 Geometry geom = (Geometry) geoms.nextElement(); 406 if ( geom != null ) { 407 if ( geom instanceof GeometryArray ) { 408 GeometryArray ga = (GeometryArray) geom; 409 if ( !( ga instanceof TriangleArray ) ) { 410 LOG.logDebug( "Not a triangle Array geometry -> convert to triangles, original type is: " + ga ); 411 GeometryInfo inf = new GeometryInfo( ga ); 412 inf.convertToIndexedTriangles(); 413 ga = inf.getGeometryArray(); 414 LOG.logDebug( "The converted type is: " + ga ); 415 } 416 417 int vertexCount = ga.getVertexCount(); 418 if ( vertexCount == 0 ) { 419 LOG.logError( "No coordinates found in the geometryArray, this may not be." ); 420 } else { 421 LOG.logDebug( "Number of vertices in shape3d: " + vertexCount ); 422 423 Element appearance = parent.getOwnerDocument().createElementNS( CITYGMLNS.toASCIIString(), 424 PRE_C + "appearance" ); 425 Element simpleTexture = parent.getOwnerDocument().createElementNS( CITYGMLNS.toASCIIString(), 426 PRE_C + "SimpleTexture" ); 427 428 int vertexFormat = ga.getVertexFormat(); 429 430 /** 431 * Textures 432 */ 433 TexCoord2f[] texCoords = null; 434 if ( ( GeometryArray.TEXTURE_COORDINATE_2 & vertexFormat ) == GeometryArray.TEXTURE_COORDINATE_2 ) { 435 LOG.logDebug( "The Geometry has a texture attached to it." ); 436 437 texCoords = new TexCoord2f[vertexCount]; 438 for ( int i = 0; i < texCoords.length; ++i ) { 439 texCoords[i] = new TexCoord2f( 0, 0 ); 440 } 441 ga.getTextureCoordinates( 0, ga.getInitialTexCoordIndex( 0 ), texCoords ); 442 StringBuilder texCoordsAsString = new StringBuilder( texCoords.length * 2 ); 443 for ( TexCoord2f coord : texCoords ) { 444 texCoordsAsString.append( coord.x ).append( " " ).append( coord.y ).append( " " ); 445 } 446 447 // TODO try to get all textures of all texture units 448 if ( app != null ) { 449 Texture tex = app.getTexture(); 450 if ( tex != null ) { 451 String texName = tex.getName(); 452 LOG.logDebug( "Texture name: " + texName ); 453 ImageComponent ic = tex.getImage( 0 ); 454 if ( ic != null ) { 455 if ( ic instanceof ImageComponent2D ) { 456 LOG.logDebug( "ImageComponent name: " + ( (ImageComponent2D) ic ).getName() ); 457 BufferedImage bi = ( (ImageComponent2D) ic ).getImage(); 458 if ( bi != null ) { 459 if ( texName == null || "".equals( texName.trim() ) ) { 460 texName = name + "_texture_" + textureCount++; 461 } 462 texName += ( ".jpg" ); 463 try { 464 File f = new File( textureOutputDir, texName ); 465 ImageIO.write( bi, "jpg", f ); 466 LOG.logDebug( "Wrote texture to: " + f.getAbsolutePath() ); 467 } catch ( IOException e ) { 468 LOG.logError( "Failed to write texture: " + texName, e ); 469 } 470 // add the texture to the appearance node 471 472 XMLTools.appendElement( simpleTexture, 473 CITYGMLNS, 474 PRE_C + "textureMap", 475 textureOutputDir + texName ); 476 XMLTools.appendElement( simpleTexture, 477 CITYGMLNS, 478 PRE_C + "textureType", 479 "specific" ); 480 XMLTools.appendElement( simpleTexture, 481 CITYGMLNS, 482 PRE_C + "repeat", 483 "0" ); 484 485 486 487 } 488 } 489 } 490 } 491 } 492 } else { 493 494 Element material = XMLTools.appendElement( appearance, CITYGMLNS, PRE_C + "Material" ); 495 XMLTools.appendElement( material, 496 CITYGMLNS, 497 PRE_C + "ambientIntensity", 498 "" + ambientIntensity ); 499 // allthough the citgml spec does not contain it, we set the ambient color :( 500 XMLTools.appendElement( material, CITYGMLNS, PRE_C + "ambientColor", ambient.x + " " 501 + ambient.y 502 + " " 503 + ambient.z ); 504 XMLTools.appendElement( material, CITYGMLNS, PRE_C + "specularColor", specular.x + " " 505 + specular.y 506 + " " 507 + specular.z ); 508 XMLTools.appendElement( material, CITYGMLNS, PRE_C + "diffuseColor", diffuse.x + " " 509 + diffuse.y 510 + " " 511 + diffuse.z ); 512 } 513 514 Point3d[] coords = new Point3d[vertexCount]; 515 for ( int i = 0; i < coords.length; ++i ) { 516 coords[i] = new Point3d( 0, 0, 0 ); 517 } 518 ga.getCoordinates( ga.getInitialVertexIndex(), coords ); 519 LOG.logDebug( "Number of coords in geometry: " + coords.length ); 520 521 // remove double defined coords (not allowed in a polygon). 522 // List<Point3d> resultingPoints = new ArrayList<Point3d>( coords.length ); 523 if ( coords != null && coords.length > 0 && coords[0] != null ) { 524 525 if ( ga instanceof TriangleArray ) { 526 // coords.length % 3 == 0 ) { 527 LOG.logInfo( "Using triangles for the coords." ); 528 // Point3d firstCoord = coords[0]; 529 // Point3d lastCoord = null; 530 int i = 0; 531 for ( ; i < coords.length; i += 3 ) { 532 Element surfaceMember = XMLTools.appendElement( parent, 533 GMLNS, 534 PRE_G + "surfaceMember" ); 535 536 Element texturedSurface = XMLTools.appendElement( surfaceMember, 537 CITYGMLNS, 538 PRE_C + "TexturedSurface" ); 539 texturedSurface.setAttribute( "orientation", "+" ); 540 541 Element baseSurface = XMLTools.appendElement( texturedSurface, 542 GMLNS, 543 PRE_G + "baseSurface" ); 544 Element polygon = XMLTools.appendElement( baseSurface, GMLNS, PRE_G + "Polygon" ); 545 Element exterior = XMLTools.appendElement( polygon, GMLNS, PRE_G + "exterior" ); 546 Element lRing = XMLTools.appendElement( exterior, GMLNS, PRE_G + "LinearRing" ); 547 if ( ( i + 3 ) <= coords.length ) { 548 for ( int j = 0; j < 3; j++ ) { 549 Point3d coord = coords[i + j]; 550 551 // use point3d.equals method to see if allready present. 552 // if ( resultingPoints.contains( coord ) ) { 553 // resultingPoints.add( coord ); 554 // check the bounds if set 555 if ( calcBounds ) { 556 if ( coord.x < lower.x ) { 557 lower.x = coord.x; 558 } 559 if ( coord.y < lower.y ) { 560 lower.y = coord.y; 561 } 562 if ( coord.z < lower.z ) { 563 lower.z = coord.z; 564 } 565 if ( coord.x > upper.x ) { 566 upper.x = coord.x; 567 } 568 if ( coord.y > upper.y ) { 569 upper.y = coord.y; 570 } 571 if ( coord.z > upper.z ) { 572 upper.z = coord.z; 573 } 574 } 575 XMLTools.appendElement( lRing, 576 GMLNS, 577 PRE_G + "pos", 578 ( translationX + coord.x ) + " " 579 + ( translationY + coord.y ) 580 + " " 581 + ( translationZ + coord.z ) ); 582 } 583 XMLTools.appendElement( lRing, 584 GMLNS, 585 PRE_G + "pos", 586 ( translationX + coords[i].x ) + " " 587 + ( translationY + coords[i].y ) 588 + " " 589 + ( translationZ + coords[i].z ) ); 590 } else {// no three points are left; 591 LOG.logInfo( "One of the geometries have an inconsistent number of points (" + ( coords.length - i ) 592 + ")to create triangles-> correcting!" ); 593 Point3d current = coords[i]; 594 Point3d previous = coords[i - 1]; 595 Point3d tmp = null; 596 switch ( coords.length - i ) { 597 case 1: // take last two nodes and create a triangle. 598 tmp = coords[i - 2]; 599 break; 600 default: // take last node and create a triangle. 601 tmp = coords[i + 1]; 602 break; 603 } 604 XMLTools.appendElement( lRing, 605 GMLNS, 606 PRE_G + "pos", 607 ( translationX + current.x ) + " " 608 + ( translationY + current.y ) 609 + " " 610 + ( translationZ + current.z ) ); 611 XMLTools.appendElement( lRing, 612 GMLNS, 613 PRE_G + "pos", 614 ( translationX + previous.x ) + " " 615 + ( translationY + previous.y ) 616 + " " 617 + ( translationZ + previous.z ) ); 618 XMLTools.appendElement( lRing, 619 GMLNS, 620 PRE_G + "pos", 621 ( translationX + tmp.x ) + " " 622 + ( translationY + tmp.y ) 623 + " " 624 + ( translationZ + tmp.z ) ); 625 XMLTools.appendElement( lRing, 626 GMLNS, 627 PRE_G + "pos", 628 ( translationX + current.x ) + " " 629 + ( translationY + current.y ) 630 + " " 631 + ( translationZ + current.z ) ); 632 633 } 634 635 // add the appearance 636 Element tmp = (Element) appearance.cloneNode( true ); 637 if ( texCoords != null && ( i + 2 ) < texCoords.length ) { 638 Element sTex = (Element) simpleTexture.cloneNode( true ); 639 XMLTools.appendElement( sTex, 640 CITYGMLNS, 641 PRE_C + "textureCoordinates", 642 texCoords[i].x + " " 643 + texCoords[i].y 644 + " " 645 + texCoords[i + 1].x 646 + " " 647 + texCoords[i + 1].y 648 + " " 649 + texCoords[i + 2].x 650 + " " 651 + texCoords[i + 2].y 652 + " " 653 + texCoords[i].x 654 + " " 655 + texCoords[i].y ); 656 tmp.appendChild( sTex ); 657 } 658 texturedSurface.appendChild( tmp ); 659 numberOfSurfaces++; 660 } 661 } else { // try to set as polygon. 662 LOG.logInfo( "Setting points as polygon." ); 663 Point3d firstCoord = coords[0]; 664 Point3d lastCoord = null; 665 Element texturedSurface = XMLTools.appendElement( parent, 666 CITYGMLNS, 667 PRE_C + "TexturedSurface" ); 668 texturedSurface.setAttribute( "orientation", "+" ); 669 670 Element baseSurface = XMLTools.appendElement( texturedSurface, 671 GMLNS, 672 PRE_G + "baseSurface" ); 673 Element polygon = XMLTools.appendElement( baseSurface, GMLNS, PRE_G + "Polygon" ); 674 Element exterior = XMLTools.appendElement( polygon, GMLNS, PRE_G + "exterior" ); 675 Element lRing = XMLTools.appendElement( exterior, GMLNS, PRE_G + "LinearRing" ); 676 677 for ( int i = 0; i < coords.length; ++i ) { 678 Point3d coord = coords[i]; 679 if ( calcBounds ) { 680 if ( coord.x < lower.x ) { 681 lower.x = coord.x; 682 } 683 if ( coord.y < lower.y ) { 684 lower.y = coord.y; 685 } 686 if ( coord.z < lower.z ) { 687 lower.z = coord.z; 688 } 689 if ( coord.x > upper.x ) { 690 upper.x = coord.x; 691 } 692 if ( coord.y > upper.y ) { 693 upper.y = coord.y; 694 } 695 if ( coord.z > upper.z ) { 696 upper.z = coord.z; 697 } 698 } 699 XMLTools.appendElement( lRing, 700 GMLNS, 701 PRE_G + "pos", 702 ( translationX + coord.x ) + " " 703 + ( translationY + coord.y ) 704 + " " 705 + ( translationZ + coord.z ) ); 706 lastCoord = coords[i]; 707 } 708 if ( !firstCoord.equals( lastCoord ) ) { 709 XMLTools.appendElement( lRing, 710 GMLNS, 711 PRE_G + "pos", 712 ( translationX + firstCoord.x ) + " " 713 + ( translationY + firstCoord.y ) 714 + " " 715 + ( translationZ + firstCoord.z ) ); 716 } 717 // XMLTools.appendElement( lRing, 718 // GMLNS, 719 // PRE_G + "pos", 720 // ( translationX + coords[i].x ) + " " 721 // + ( translationY + coords[i].y ) 722 // + " " 723 // + ( translationZ + coords[i].z ) ); 724 // } 725 726 // add the appearance 727 Element tmp = (Element) appearance.cloneNode( true ); 728 texturedSurface.appendChild( tmp ); 729 } 730 731 } 732 733 /** 734 * NORMALS 735 */ 736 if ( ( GeometryArray.NORMALS & vertexFormat ) == GeometryArray.NORMALS ) { 737 LOG.logDebug( "The Geometry has normals, but they are not exported (yet)." ); 738 // Vector3f[] normals = new Vector3f[vertexCount]; 739 // for ( int i = 0; i < normals.length; ++i ) { 740 // normals[i] = new Vector3f( 0, 0, 0 ); 741 // } 742 // ga.getNormals( 0, normals ); 743 // for ( Vector3f normal : normals ) { 744 // parent.append( normal ).append( " " ); 745 // } 746 } 747 /** 748 * Colors 3f 749 */ 750 if ( ( GeometryArray.COLOR_3 & vertexFormat ) == GeometryArray.COLOR_3 ) { 751 LOG.logDebug( "The Geometry has Colors per Vertex with 3 chanels, but they are not exported (yet)." ); 752 // Color3f[] colors = new Color3f[vertexCount]; 753 // for ( int i = 0; i < colors.length; ++i ) { 754 // colors[i] = new Color3f( 0, 0, 0 ); 755 // } 756 // ga.getColors( 0, colors ); 757 // parent.append( "\n - Colors3f (" ).append( numberOfGeom ).append( "): " ); 758 // for ( Color3f color : colors ) { 759 // parent.append( color ).append( " " ); 760 // } 761 } 762 763 /** 764 * Colors 4f 765 */ 766 if ( ( GeometryArray.COLOR_4 & vertexFormat ) == GeometryArray.COLOR_4 ) { 767 LOG.logDebug( "The Geometry has Colors per Vertex with 4 chanels, but they are not exported (yet)." ); 768 // Color4f[] colors = new Color4f[vertexCount]; 769 // for ( int i = 0; i < colors.length; ++i ) { 770 // colors[i] = new Color4f( 0, 0, 0, 0 ); 771 // } 772 // ga.getColors( 0, colors ); 773 // parent.append( "\n - Colors4f (" ).append( numberOfGeom ).append( "): " ); 774 // for ( Color4f color : colors ) { 775 // parent.append( color ).append( " " ); 776 // } 777 } 778 779 // Element appearance = XMLTools.appendElement( texturedSurface, 780 // CITYGMLNS, 781 // PRE_C + "appearance" ); 782 } 783 } else { 784 LOG.logInfo( "Only Shape3d Objects with geometry rasters are supported for outputing" ); 785 } 786 } 787 } 788 789 } 790 791 /** 792 * Don't know how to export the background yet. 793 * 794 * @param b 795 * leaf of the j3dScene 796 * @param parent 797 * to append to. 798 */ 799 private void outputBackground( @SuppressWarnings("unused") 800 Background b, @SuppressWarnings("unused") 801 Element parent ) { 802 LOG.logError( "Cannot output a background yet." ); 803 } 804 805 public String getName() { 806 return "CityGML exporter"; 807 } 808 809 public String getShortDescription() { 810 return "Convert a given j3d:BranchGroup to CityGML LOD1"; 811 } 812 813 /** 814 * @param args 815 */ 816 public static void main( String[] args ) { 817 if ( args.length == 0 ) { 818 outputHelp(); 819 } 820 Map<String, String> params = new HashMap<String, String>( 5 ); 821 for ( int i = 0; i < args.length; i++ ) { 822 String arg = args[i]; 823 if ( args != null && !"".equals( arg.trim() ) ) { 824 arg = arg.trim(); 825 if ( arg.equalsIgnoreCase( "-?" ) || arg.equalsIgnoreCase( "-h" ) ) { 826 outputHelp(); 827 } else { 828 if ( i + 1 < args.length ) { 829 String val = args[++i]; 830 if ( val != null && !"".equals( val.trim() ) ) { 831 params.put( arg.toLowerCase(), val.trim() ); 832 } else { 833 System.out.println( "Invalid value for parameter: " + arg ); 834 } 835 } else { 836 System.out.println( "No value for parameter: " + arg ); 837 } 838 } 839 } 840 } 841 842 String fileName = params.get( "-infile" ); 843 if ( fileName == null || "".equals( fileName.trim() ) ) { 844 System.out.println( "\n\n" ); 845 LOG.logError( "The -inFile parameter must be defined." ); 846 System.out.println( "\n\n" ); 847 outputHelp(); 848 } 849 String name = fileName; 850 int last = fileName.lastIndexOf( File.separator ); 851 if ( last != -1 ) { 852 name = fileName.substring( last + 1 ); 853 last = name.lastIndexOf( "." ); 854 if ( last != -1 ) { 855 name = name.substring( 0, last ); 856 } 857 858 } 859 860 String texDir = params.get( "-texdir" ); 861 if ( texDir == null || "".equals( texDir.trim() ) ) { 862 System.out.println( "\n\n" ); 863 LOG.logError( "The -texdir parameter must be defined." ); 864 System.out.println( "\n\n" ); 865 outputHelp(); 866 } 867 868 VrmlLoader loader = new VrmlLoader(); 869 try { 870 double xTrans = Double.NaN; 871 String t = params.get( "-xtranslation" ); 872 if ( t != null && !"".equals( t ) ) { 873 try { 874 xTrans = Double.parseDouble( t ); 875 } catch ( NumberFormatException nfe ) { 876 // nottin 877 } 878 } 879 double yTrans = Double.NaN; 880 t = params.get( "-ytranslation" ); 881 if ( t != null && !"".equals( t ) ) { 882 try { 883 yTrans = Double.parseDouble( t ); 884 } catch ( NumberFormatException nfe ) { 885 // nottin 886 } 887 } 888 double zTrans = Double.NaN; 889 t = params.get( "-ztranslation" ); 890 if ( t != null && !"".equals( t ) ) { 891 try { 892 zTrans = Double.parseDouble( t ); 893 } catch ( NumberFormatException nfe ) { 894 // nottin 895 } 896 } 897 boolean asWFS = false; 898 t = params.get( "-wfstransaction" ); 899 if ( t != null && !"".equals( t ) ) { 900 if ( t.equalsIgnoreCase( "y" ) || t.equalsIgnoreCase( "yes" ) ) { 901 asWFS = true; 902 } 903 } 904 J3DToCityGMLExporter converter = new J3DToCityGMLExporter( name, 905 params.get( "-srs" ), 906 params.get( "-buildingFunction" ), 907 texDir, 908 xTrans, 909 yTrans, 910 zTrans, 911 asWFS ); 912 System.out.println( "\n\nTrying to load scene, if you see no message beneath this message, please check your vrml file: " + fileName 913 + " if the refrerred textures are java qouted, e.g. in Windows \\\\ should be used instead of a single \\" ); 914 Scene scene = loader.load( fileName ); 915 if ( scene != null ) { 916 System.out.println( "Successfully loaded the scene." ); 917 BranchGroup bg = scene.getSceneGroup(); 918 if ( bg != null && bg.numChildren() > 0 ) { 919 System.out.println( "Retrieval of the j3d.Branchgroup was successful." ); 920 StringBuilder output = new StringBuilder( 20000 ); 921 converter.export( output, bg ); 922 String outFile = params.get( "-outfile" ); 923 if ( outFile == null || "".equals( outFile.trim() ) ) { 924 System.out.println( output.toString() ); 925 } else { 926 BufferedWriter fWriter = new BufferedWriter( new FileWriter( new File( outFile ) ) ); 927 fWriter.write( output.toString() ); 928 fWriter.flush(); 929 fWriter.close(); 930 } 931 } else { 932 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: " + fileName 933 + ")" ); 934 } 935 } else { 936 System.out.println( "The scene was not loaded (did you correctly quoted the texture paths in your vrml-file: " + fileName 937 + ")" ); 938 } 939 } catch ( FileNotFoundException e ) { 940 LOG.logDebug( "Could not load vrm-file because: " + e.getMessage(), e ); 941 } catch ( IncorrectFormatException e ) { 942 LOG.logDebug( "Could not load vrm-file because: " + e.getMessage(), e ); 943 } catch ( ParsingErrorException e ) { 944 LOG.logDebug( "Could not load vrm-file because: " + e.getMessage(), e ); 945 } catch ( IOException e ) { 946 LOG.logDebug( "Could not create converter because: " + e.getMessage(), e ); 947 } catch ( Exception e ) { 948 LOG.logDebug( "Error while loading the file: " + fileName + " because: " + e.getMessage(), e ); 949 } catch ( Throwable t ) { 950 LOG.logDebug( "Error while loading the file: " + fileName + " because: " + t.getMessage(), t ); 951 } 952 } 953 954 private static void outputHelp() { 955 StringBuilder sb = new StringBuilder(); 956 sb.append( "The J3DToCityGMLExported program can be used to export a j3d branchgroup created from a vrml-file to citygml. \n" ); 957 sb.append( "Following parameters are supported:\n" ); 958 sb.append( "-inFile the /path/to/vrml-file\n" ); 959 sb.append( "-texDir directory to output the found textures to.\n" ); 960 sb.append( "[-outFile] the /path/to/the/output/file or standard output if not supplied.\n" ); 961 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" ); 962 sb.append( "[-buildingFunction] the function this citygml building will have (if omitted 1001 will be used).\n" ); 963 sb.append( "[-srs] the coordinateSystem used for the citygml (if omitted 'EPSG:31466' will be used).\n" ); 964 sb.append( "[-xTranslation] the amount to add up to the x part of the found coordinates(if omitted '0' will be used).\n" ); 965 sb.append( "[-yTranslation] the amount to add up to the y part of the found coordinates(if omitted '0' will be used).\n" ); 966 sb.append( "-?|-h output this text\n" ); 967 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" ); 968 sb.append( "outputing to cool_tower.xml in the directory my_output_dir (relative to the current dir)\n" ); 969 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" ); 970 System.out.println( sb.toString() ); 971 System.exit( 1 ); 972 } 973 974 }