//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/ogcwebservices/wpvs/util/Attic/ResolutionStripe.java,v 1.5 2006/12/04 17:06:43 bezema Exp $ /*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: EXSE, Department of Geography, University of Bonn http://www.giub.uni-bonn.de/exse/ lat/lon GmbH http://www.lat-lon.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Andreas Poth lat/lon GmbH Aennchenstrasse 19 53177 Bonn Germany E-Mail: poth@lat-lon.de Jens Fitzke lat/lon GmbH Aennchenstrasse 19 53177 Bonn Germany E-Mail: jens.fitzke@uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.ogcwebservices.wpvs.util; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.font.TextLayout; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import javax.imageio.ImageIO; import javax.media.j3d.BranchGroup; import javax.vecmath.Vector3f; import org.deegree.framework.util.MapUtils; import org.deegree.model.crs.CoordinateSystem; import org.deegree.model.spatialschema.Envelope; import org.deegree.model.spatialschema.GeometryException; import org.deegree.model.spatialschema.GeometryFactory; import org.deegree.model.spatialschema.Point; import org.deegree.model.spatialschema.Position; import org.deegree.model.spatialschema.Surface; import org.deegree.model.spatialschema.WKTAdapter; import org.deegree.ogcwebservices.OGCWebServiceException; import org.deegree.ogcwebservices.wpvs.GetViewServiceInvoker; import org.deegree.ogcwebservices.wpvs.WCSInvoker; import org.deegree.ogcwebservices.wpvs.WFSInvoker; import org.deegree.ogcwebservices.wpvs.WMSInvoker; import org.deegree.ogcwebservices.wpvs.configuration.AbstractDataSource; import org.deegree.ogcwebservices.wpvs.j3d.DefaultSurface; import org.deegree.ogcwebservices.wpvs.j3d.TerrainModel; import org.deegree.ogcwebservices.wpvs.j3d.TexturedHeightMapTerrain; import org.deegree.ogcwebservices.wpvs.j3d.TriangleTerrain; import org.deegree.processing.raster.converter.Image2RawData; import org.j3d.geom.GeometryData; /** * The <code>ResolutionStripe</code> class encapsulates a Surface with a maximum Resolution, which * is convenient for the creation of a quadtree. * * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> * * @author last edited by: $Author: bezema $ * * @version $Revision: 1.5 $, $Date: 2006/12/04 17:06:43 $ * */ public class ResolutionStripe implements Callable<ResolutionStripe> { private final double maxResolution; private final double minResolution; private Surface surface; private TerrainModel elevationModel; private AbstractDataSource elevationModelDataSource = null; private HashMap<String, BufferedImage> textures; private ArrayList<AbstractDataSource> texturesDataSources; private HashMap<String, DefaultSurface> features; private ArrayList<AbstractDataSource> featureCollectionDataSources; private double minimalHeightLevel; private String outputFormat = null; private BranchGroup resultingJ3DScene; /** * @param surface * @param maximumResolution * @param minimumResolution * @param minimalHeight */ public ResolutionStripe( Surface surface, double maximumResolution, double minimumResolution, double minimalHeight ) { this.surface = surface; this.maxResolution = maximumResolution; this.minResolution = minimumResolution; this.minimalHeightLevel = minimalHeight; featureCollectionDataSources = new ArrayList<AbstractDataSource>( 5 ); texturesDataSources = new ArrayList<AbstractDataSource>( 5 ); textures = new HashMap<String, BufferedImage>( 10 ); features = new HashMap<String, DefaultSurface>( 1000 ); resultingJ3DScene = new BranchGroup(); } /** * @param surface * @param maximumResolution * @param minimumResolution * @param minimalHeight * @param outputFormat */ public ResolutionStripe( Surface surface, double maximumResolution, double minimumResolution, double minimalHeight, String outputFormat ) { this( surface, maximumResolution, minimumResolution, minimalHeight ); this.outputFormat = outputFormat; } /** * @return the CRS of this ResolutionStripe */ public CoordinateSystem getCRSName() { return surface.getCoordinateSystem(); } /** * @return the resolution of the largest stripe in the surface. */ public double getMaxResolution() { return maxResolution; } /** * @return the (always possitive) resolution of the largest (away from the viewer) side of the * surface as scale denominator, which means divide by * {@link MapUtils#DEFAULT_PIXEL_SIZE}. */ public double getMaxResolutionAsScaleDenominator() { return Math.abs( maxResolution ) / MapUtils.DEFAULT_PIXEL_SIZE; } /** * @return the (always possitive) resolution of the smallest (towards the viewer) side of the * surface as scale denominator, which means divide by * {@link MapUtils#DEFAULT_PIXEL_SIZE}. */ public double getMinResolutionAsScaleDenominator() { return Math.abs( minResolution ) / MapUtils.DEFAULT_PIXEL_SIZE; } /** * @return the resolution of the smallest stripe in the surface. Attention the resolution may be * negative if the requested data is "behind" the viewer. */ public double getMinResolution() { return minResolution; } /** * @return the geometric surface which is defines this resolutionStripe. */ public Surface getSurface() { return surface; } /** * @return the minimalTerrainHeight. */ public double getMinimalTerrainHeight() { return minimalHeightLevel; } /** * @return the requestsize for the bbox containing this resolutionsstripe. */ public int getRequestWidthForBBox() { return (int) ( surface.getEnvelope().getWidth() / Math.abs( maxResolution ) ); } /** * @return the height (in pixels) of the request envelope */ public int getRequestHeightForBBox() { return (int) ( surface.getEnvelope().getHeight() / Math.abs( maxResolution ) ); } /** * @return the elevationModel. */ public TerrainModel getElevationModel() { return elevationModel; } /** * @param elevationModel * An other elevationModel. */ public void setElevationModel( TerrainModel elevationModel ) { this.elevationModel = elevationModel; } /** * @param pointsList * containing Points which represents the heights of measures points (normally * aquired from a wfs). */ public void setElevationModelFromMeassurePoints( List<Point> pointsList ) { Envelope env = surface.getEnvelope(); double width = env.getWidth(); double height = env.getHeight(); Point lowerLeft = GeometryFactory.createPoint( env.getMin().getX(), env.getMin().getY(), 55, env.getCoordinateSystem() ); Point upperRight = GeometryFactory.createPoint( env.getMax().getX(), env.getMax().getY(), 55, env.getCoordinateSystem() ); Point lowerRight = GeometryFactory.createPoint( lowerLeft.getX() + width, lowerLeft.getY(), lowerLeft.getZ(), env.getCoordinateSystem() ); Point upperLeft = GeometryFactory.createPoint( lowerLeft.getX(), lowerLeft.getY() + height, lowerLeft.getZ(), env.getCoordinateSystem() ); pointsList.add( lowerLeft ); pointsList.add( upperRight ); pointsList.add( lowerRight ); pointsList.add( upperLeft ); VisADWrapper vw = new VisADWrapper( pointsList ); List<float[][]> triangles = vw.getTriangleCollectionAsList(); elevationModel = new TriangleTerrain( triangles, surface.getEnvelope() ); } /** * @param heightMap * a BufferedImage which contains height values, normally aquired from a wcs. */ public void setElevationModelFromHeightMap( BufferedImage heightMap ) { Image2RawData i2rd = new Image2RawData( heightMap ); float[][] heights = i2rd.parse(); Envelope env = surface.getEnvelope(); Position lowerLeft = surface.getEnvelope().getMin(); Vector3f lLeft = new Vector3f( (float) lowerLeft.getX(), (float) lowerLeft.getY(), 0 ); // Triangles won't work -> an error in org.j3d.geom.terrain.ElevationGridGenerator therefor // using QUADS elevationModel = new TexturedHeightMapTerrain( (float) env.getWidth(), (float) env.getHeight(), heights, lLeft, GeometryData.QUADS, false ); } /** * @return the features of this resolutionstripe */ public HashMap<String, DefaultSurface> getFeatures() { return features; } /** * @param key * the name of the feature to be added. * @param feature * (e.g a building, tree etc.) as a DefaultSurface (derived frome Shape3D) to be * added to the hashmap. * @return true if the feature wasn't allready defined in the hashmap and could therefore be * inserted, or if the key or feature are null. */ public boolean addFeature( String key, DefaultSurface feature ) { if ( feature != null && key != null ) { DefaultSurface tmp = features.get( key ); if ( tmp == null && !features.containsKey( key ) ) { features.put( key, feature ); return true; } } return false; } /** * @return the textures value. */ public HashMap<String, BufferedImage> getTextures() { return textures; } /** * @param key * the name of the texture to be added. * @param texture * to be added to the hashmap. * @return true if the texture wasn't allready defined in the hashmap and could therefore be * inserted, or if the key or texture are null. */ public boolean addTexture( String key, BufferedImage texture ) { if ( texture != null && key != null ) { BufferedImage tmp = textures.get( key ); if ( tmp == null && !textures.containsKey( key ) ) { textures.put( key, texture ); return true; } } return false; } /** * @return the elevationModelDataSource value. */ public AbstractDataSource getElevationModelDataSource() { return elevationModelDataSource; } /** * @param elevationModelDataSource * An other elevationModelDataSource value. */ public void setElevationModelDataSource( AbstractDataSource elevationModelDataSource ) { this.elevationModelDataSource = elevationModelDataSource; } /** * @return the featureCollectionDataSources value. */ public ArrayList<AbstractDataSource> getFeatureCollectionDataSources() { return featureCollectionDataSources; } /** * @param featureCollectionDataSource * a DataSources for a specific featureCollection. */ public void addFeatureCollectionDataSource( AbstractDataSource featureCollectionDataSource ) { if ( featureCollectionDataSource != null ) { if ( !featureCollectionDataSources.contains( featureCollectionDataSource ) ) { featureCollectionDataSources.add( featureCollectionDataSource ); } } } /** * @return the texturesDataSources value. */ public ArrayList<AbstractDataSource> getTexturesDataSources() { return texturesDataSources; } /** * @param textureDataSource * An other texturesDataSources value. */ public void addTextureDataSource( AbstractDataSource textureDataSource ) { if ( textureDataSource != null ) { if ( !texturesDataSources.contains( textureDataSource ) ) { texturesDataSources.add( textureDataSource ); } } } /** * * @return the OutputFormat of the resultImage */ public String getOutputFormat() { return outputFormat; } /** * @param outputFormat * the mime type of the resultimage */ public void setOutputFormat( String outputFormat ) { this.outputFormat = outputFormat; } /** * After a call to this class call method, it is possible to get a Java3D representation --in * form of a BranchGroup-- of this resolutionStripe. In this BranchGroup all the textures and * requested features are added to the ElevationModel. * * @return a Java3D representation of this ResolutionStripe. */ public BranchGroup getJava3DRepresentation() { return resultingJ3DScene; } /** * This call method is part of the Deegree Concurrent framework ({@link org.deegree.framework.concurrent.Executor}) . * In this case it requests all the Data for a <tt>ResolutionStripe</tt> by invoking the * necessary webservices. * * @see java.util.concurrent.Callable#call() */ public ResolutionStripe call() throws OGCWebServiceException { int invokeCounter = 0; // Strictly the different datasources must not be separated into two different // DataSourceList, it might be handy (for caching) to do so though. for ( AbstractDataSource textureDS : texturesDataSources ) { invokeDataSource( textureDS, invokeCounter++ ); } for ( AbstractDataSource featureDS : featureCollectionDataSources ) { invokeDataSource( featureDS, invokeCounter++ ); } if ( elevationModelDataSource != null ) { invokeDataSource( elevationModelDataSource, -1 ); } else { elevationModel = new TriangleTerrain( createTrianglesFromBBox(), surface.getEnvelope() ); } createJava3DRepresentation(); return this; } private void invokeDataSource( AbstractDataSource ads, int id ) { try { GetViewServiceInvoker invoker = null; if ( ads.getServiceType() == AbstractDataSource.LOCAL_WMS || ads.getServiceType() == AbstractDataSource.REMOTE_WMS ) { invoker = new WMSInvoker( this, id ); } else if ( ads.getServiceType() == AbstractDataSource.LOCAL_WCS || ads.getServiceType() == AbstractDataSource.REMOTE_WCS ) { invoker = new WCSInvoker( this, id, outputFormat, ( ads == elevationModelDataSource ) ); } else { // WFS -> was checked in DefaultGetViewHandler invoker = new WFSInvoker( this, id, ( ads == elevationModelDataSource ) ); } invoker.invokeService( ads ); } catch ( Exception e ) { System.out.println( "\tError after invoking AbstractDataSource in ResolutonStripe: " + e.getLocalizedMessage() ); // e.printStackTrace(); } } private List<float[][]> createTrianglesFromBBox() { ArrayList<float[][]> triangles = new ArrayList<float[][]>( 2 ); Envelope env = surface.getEnvelope(); double width = env.getWidth(); double height = env.getHeight(); Position lowerLeft = env.getMin(); Position upperRight = env.getMax(); Position lowerRight = GeometryFactory.createPosition( lowerLeft.getX() + width, lowerLeft.getY(), lowerLeft.getZ() ); Position upperLeft = GeometryFactory.createPosition( lowerLeft.getX(), lowerLeft.getY() + height, lowerLeft.getZ() ); float zValue = new Double( lowerLeft.getZ() ).floatValue(); if ( Float.isNaN( zValue ) ) { zValue = new Double( minimalHeightLevel ).floatValue(); if ( Float.isNaN( zValue ) ) zValue = 0f; } float[][] triangle = new float[3][3]; triangle[0][0] = (float) lowerLeft.getX(); triangle[0][1] = (float) lowerLeft.getY(); triangle[0][2] = zValue; triangle[1][0] = (float) upperRight.getX(); triangle[1][1] = (float) upperRight.getY(); triangle[1][2] = zValue; triangle[2][0] = (float) upperLeft.getX(); triangle[2][1] = (float) upperLeft.getY(); triangle[2][2] = zValue; triangles.add( triangle ); triangle = new float[3][3]; triangle[0][0] = (float) lowerLeft.getX(); triangle[0][1] = (float) lowerLeft.getY(); triangle[0][2] = zValue; triangle[1][0] = (float) lowerRight.getX(); triangle[1][1] = (float) lowerRight.getY(); triangle[1][2] = zValue; triangle[2][0] = (float) upperRight.getX(); triangle[2][1] = (float) upperRight.getY(); triangle[2][2] = zValue; triangles.add( triangle ); return triangles; } /** * */ private void createJava3DRepresentation() { if ( elevationModel == null ) { elevationModel = new TriangleTerrain( createTrianglesFromBBox(), surface.getEnvelope() ); } // add the maps as textures to the elevationModel. Collection<BufferedImage> textureImages = textures.values(); if ( textureImages != null ) { // create texture as BufferedImage BufferedImage texture = new BufferedImage( getRequestWidthForBBox(), getRequestHeightForBBox(), BufferedImage.TYPE_INT_ARGB ); Graphics2D g2d = (Graphics2D) texture.getGraphics(); if ( textureImages.size() > 0 ) { for ( BufferedImage tex : textureImages ) { g2d.drawImage( tex, 0, 0, null ); } // paintString( g2d, Double.toString( minResolution ) ); } else { paintString( g2d, "An error occurred, or no image" ); } elevationModel.setTexture( texture ); } elevationModel.createTerrain(); // If the elevation model is a heightmap (wcs) the 3d scene must be transformed to the // scene's coordinates {@link ResolutionStripe#setElevationModelFromHeightMap} // if ( elevationTransform != null ) { // elevationTransform.addChild( elevationModel ); // resultingJ3DScene.addChild( elevationTransform ); // } else { resultingJ3DScene.addChild( elevationModel ); // } // add the features to the elevationModel Collection<DefaultSurface> featureSurfaces = features.values(); if ( featureSurfaces != null ) { for ( DefaultSurface ds : featureSurfaces ) { resultingJ3DScene.addChild( ds ); } } resultingJ3DScene.compile(); } private void paintString( Graphics2D g2d, String stringToPaint ) { Font originalFont = g2d.getFont(); float originalFontSize = originalFont.getSize(); TextLayout tl = new TextLayout( stringToPaint, originalFont, g2d.getFontRenderContext() ); Rectangle2D r2d = tl.getBounds(); double width = r2d.getWidth(); double height = r2d.getHeight(); double requestWidth = getRequestWidthForBBox(); double requestHeight = getRequestHeightForBBox(); // little widther than the requestwidth ensures total readabillity double approx = requestWidth / ( width * 1.2 ); originalFont = originalFont.deriveFont( (float) ( originalFontSize * approx ) ); tl = new TextLayout( stringToPaint, originalFont, g2d.getFontRenderContext() ); r2d = tl.getBounds(); width = r2d.getWidth(); height = r2d.getHeight(); int x = (int) Math.round( ( requestWidth * 0.5 ) - ( width * 0.5 ) ); int y = (int) Math.round( ( requestHeight * 0.5 ) + ( height * 0.5 ) ); g2d.setColor( Color.GRAY ); g2d.drawRect( 0, 0, (int) requestWidth, (int) requestHeight ); g2d.setColor( Color.RED ); g2d.setFont( originalFont ); g2d.drawString( stringToPaint, x, y ); } @Override public String toString() { StringBuffer sb = new StringBuffer( "Resolution: " + maxResolution + "\n" ); try { sb.append( WKTAdapter.export( this.surface ) ); } catch ( GeometryException e ) { e.printStackTrace(); } return sb.toString(); } /** * @return a well known representation of the geometry of this Resolutionstripe */ public String toWKT() { try { return new StringBuffer( WKTAdapter.export( this.surface ) ).toString(); } catch ( GeometryException e ) { e.printStackTrace(); return new String( "" ); } } /** * Outputs the textures to the tmp directory with following format: * <code>key_response:___res:_maxresolution__random_id.jpg</code> this file will be deleted at * jvm termination. */ public void outputTextures() { Set<String> keys = textures.keySet(); for ( String key : keys ) { try { // System.out.println( "saving image" ); File f = File.createTempFile( key + "_response:_" + "__res:_" + maxResolution + "___", ".jpg" ); f.deleteOnExit(); // System.out.println( f ); // ImageUtils.saveImage( responseImage, f, 1 ); ImageIO.write( textures.get( key ), "jpg", f ); } catch ( IOException e ) { e.printStackTrace(); } } } } /*************************************************************************************************** * Changes to this class. What the people have been up to: ----------------------------------------- * $Log: ResolutionStripe.java,v $ * Revision 1.5 2006/12/04 17:06:43 bezema * enhanced dgm from wcs support * Revision 1.4 2006/11/30 11:26:27 bezema working on the raster * heightmap elevationmodel Revision 1.3 2006/11/28 16:53:45 bezema bbox resolution works plus clean * up and javadoc * * Revision 1.2 2006/11/27 15:43:11 bezema Updated the coordinatesystem handling * * Revision 1.1 2006/11/23 11:46:40 bezema The initial version of the new wpvs * **************************************************************************************************/