001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/io/geotiff/GeoTiffWriter.java $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 and 007 lat/lon GmbH 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 037 package org.deegree.io.geotiff; 038 039 import java.awt.image.BufferedImage; 040 import java.io.IOException; 041 import java.io.OutputStream; 042 import java.util.ArrayList; 043 import java.util.HashMap; 044 import java.util.List; 045 import java.util.Set; 046 047 import org.deegree.framework.log.ILogger; 048 import org.deegree.framework.log.LoggerFactory; 049 import org.deegree.model.crs.CoordinateSystem; 050 import org.deegree.model.spatialschema.Envelope; 051 052 import com.sun.media.jai.codec.TIFFEncodeParam; 053 import com.sun.media.jai.codec.TIFFField; 054 import com.sun.media.jai.codecimpl.TIFFImageEncoder; 055 056 /** 057 * This class is for writing GeoTIFF files from any java.awt.image. At that time, only writing the Bounding Box is 058 * available. 059 * 060 * 061 * @author <a href="mailto:schaefer@lat-lon.de">Axel Schaefer </A> 062 * @author last edited by: $Author: mschneider $ 063 * @version 2.0. $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18 Jun 2009) $ 064 * @since 2.0 065 */ 066 public class GeoTiffWriter { 067 068 private static final ILogger LOG = LoggerFactory.getLogger( GeoTiffWriter.class ); 069 070 // GeoTIFF values for GTModelTypeGeoKey 071 // http://www.remotesensing.org/geotiff/spec/geotiff6.html#6.3.1.1 072 private static final int ValueModelTypeProjected = 1; 073 private static final int ValueModelTypeGeographic = 2; 074 075 076 private List<TIFFField> tiffields = null; 077 078 /** 079 * see http://www.remotesensing.org/geotiff/spec/geotiff2.4.html#2.4 especialy the KeyEntry part. 080 * 081 * The key (Integer) is the GeoTIFF Key ID http://www.remotesensing.org/geotiff/spec/geotiff6.html#6.2 The value is 082 * an int array of the size 3. If you want to store simple GeoTIFF key-values set the array to { 0, 1, VALUE }. 083 */ 084 private HashMap<Integer, int[]> geoKeyDirectoryTag = null; 085 086 private BufferedImage bi = null; 087 088 private double offset = 0; 089 090 private double scaleFactor = 1; 091 092 /** 093 * creates an GeoTiffWriter instance from an java.awt.image. 094 * 095 * @param image 096 * the image, to be transformed to a GeoTIFF. 097 * @param envelope 098 * the BoundingBox, the GeoTIFF should have 099 * @param resx 100 * The X-Resolution 101 * @param resy 102 * The Y-Resolution 103 * @param crs 104 */ 105 public GeoTiffWriter( BufferedImage image, Envelope envelope, double resx, double resy, CoordinateSystem crs ) { 106 this( image, envelope, resx, resy, crs, 0, 1 ); 107 } 108 109 /** 110 * creates an GeoTiffWriter instance from an java.awt.image. 111 * 112 * @param image 113 * the image, to be transformed to a GeoTIFF. 114 * @param envelope 115 * the BoundingBox, the GeoTIFF should have 116 * @param resx 117 * The X-Resolution 118 * @param resy 119 * The Y-Resolution 120 * @param crs 121 * @param offset 122 * @param scaleFactor 123 */ 124 public GeoTiffWriter( BufferedImage image, Envelope envelope, double resx, double resy, CoordinateSystem crs, 125 double offset, double scaleFactor ) { 126 this.tiffields = new ArrayList<TIFFField>(); 127 this.geoKeyDirectoryTag = new HashMap<Integer, int[]>(); 128 int[] header = { 1, 2, 0 }; 129 this.bi = image; 130 this.offset = offset; 131 this.scaleFactor = scaleFactor; 132 // sets the header. this key must be overwritten in the write-method. 133 addKeyToGeoKeyDirectoryTag( 1, header ); 134 // sets the boundingbox (with envelope and resolution) 135 setBoxInGeoTIFF( envelope, resx, resy ); 136 // sets the CoordinateSystem 137 setCoordinateSystem( crs ); 138 } 139 140 /** 141 * returns the GeoKeys as an array of Tiff Fields. 142 * 143 * @return an array of TIFFFields 144 */ 145 private TIFFField[] getGeoTags() { 146 TIFFField[] extraFields = null; 147 148 if ( this.tiffields != null && this.tiffields.size() > 0 ) { 149 extraFields = new TIFFField[this.tiffields.size()]; 150 for ( int i = 0; i < extraFields.length; i++ ) { 151 extraFields[i] = this.tiffields.get( i ); 152 } 153 } 154 return extraFields; 155 } 156 157 /** 158 * gets the GeoKeyDirectoryTag as a chararrary. 159 * 160 * @return the GeoKeyDirectoryTag as a chararrary 161 */ 162 private char[] getGeoKeyDirectoryTag() { 163 char[] ch = null; 164 165 // check, if it contains more fields than the header 166 if ( this.geoKeyDirectoryTag.size() > 1 ) { 167 ch = new char[this.geoKeyDirectoryTag.size() * 4]; 168 Set set = this.geoKeyDirectoryTag.keySet(); 169 Object[] o = set.toArray(); 170 171 Integer keyID = null; 172 int[] temparray = new int[3]; 173 174 // o.length is equals this.geoKeyDirectoryTag.size() 175 for ( int i = 0; i < o.length; i++ ) { 176 // get the key-ID from the ObjectArray 'o' 177 keyID = (Integer) o[i]; 178 // get the values of the HashMap (int[]) at the key keyID 179 temparray = this.geoKeyDirectoryTag.get( keyID ); 180 ch[i * 4] = (char) keyID.intValue(); 181 ch[i * 4 + 1] = (char) temparray[0]; 182 ch[i * 4 + 2] = (char) temparray[1]; 183 ch[i * 4 + 3] = (char) temparray[2]; 184 } 185 } 186 187 return ch; 188 } 189 190 /** 191 * 192 * @param key 193 * @param values 194 */ 195 private void addKeyToGeoKeyDirectoryTag( int key, int[] values ) { 196 this.geoKeyDirectoryTag.put( new Integer( key ), values ); 197 } 198 199 /** 200 * Writes the GeoTIFF as a BufferedImage to an OutputStream. The OutputStream isn't closed after the method. 201 * 202 * @param os 203 * the output stream, which has to be written. 204 * @throws IOException 205 */ 206 public void write( OutputStream os ) 207 throws IOException { 208 if ( this.geoKeyDirectoryTag.size() > 1 ) { 209 // overwrite header with *real* size of GeoKeyDirectoryTag 210 int[] header = { 1, 2, this.geoKeyDirectoryTag.size() - 1 }; 211 addKeyToGeoKeyDirectoryTag( 1, header ); 212 213 char[] ch = getGeoKeyDirectoryTag(); 214 215 // int tag, int type, int count, java.lang.Object data 216 TIFFField geokeydirectorytag = new TIFFField( GeoTiffTag.GeoKeyDirectoryTag, TIFFField.TIFF_SHORT, 217 ch.length, ch ); 218 this.tiffields.add( geokeydirectorytag ); 219 } 220 221 // get the geokeys 222 TIFFField[] tiffields_array = getGeoTags(); 223 224 TIFFEncodeParam encodeParam = new TIFFEncodeParam(); 225 if ( tiffields_array != null && tiffields_array.length > 0 ) { 226 encodeParam.setExtraFields( tiffields_array ); 227 } 228 TIFFImageEncoder encoder = new TIFFImageEncoder( os, encodeParam ); 229 230 // void encoder( java.awt.image.RenderedImage im ) 231 encoder.encode( bi ); 232 } 233 234 // ************************************************************************ 235 // BoundingBox 236 // ************************************************************************ 237 /** 238 * description: Extracts the GeoKeys of the GeoTIFF. The Following Tags will be 239 * extracted(http://www.remotesensing.org/geotiff/spec/geotiffhome.html): 240 * <ul> 241 * <li>ModelPixelScaleTag = 33550 (SoftDesk) 242 * <li>ModelTiepointTag = 33922 (Intergraph) 243 * </ul> 244 * implementation status: working 245 */ 246 private void setBoxInGeoTIFF( Envelope envelope, double resx, double resy ) { 247 248 double[] resolution = { resx, resy, 1d / scaleFactor }; 249 // ModelPixelScaleTag: 250 // Tag = 33550 251 // Type = DOUBLE (IEEE Double precision) 252 // N = 3 253 // Owner: SoftDesk 254 TIFFField modelPixelScaleTag = new TIFFField( GeoTiffTag.ModelPixelScaleTag, TIFFField.TIFF_DOUBLE, 3, 255 resolution ); 256 257 this.tiffields.add( modelPixelScaleTag ); 258 259 // ModelTiepointTag: 260 // calculate the first points for the upper-left corner {0,0,0} of the 261 // tiff 262 double tp_01x = 0.0; // (0, val1) 263 double tp_01y = 0.0; // (1, val2) 264 double tp_01z = 0.0; // (2) z-value. not needed 265 266 // the real-world coordinates for the upper points (tp_01.) 267 // these are the unknown variables which have to be calculated. 268 double tp_02x = 0.0; // (3, val4) 269 double tp_02y = 0.0; // (4, val5) 270 double tp_02z = -offset; // (5) z-value. not needed 271 272 double xmin = envelope.getMin().getX(); 273 double ymax = envelope.getMax().getY(); 274 275 // transform this equation: xmin = ?[val4] - ( tp_01x * resx ) 276 tp_02x = xmin + ( tp_01x * resx ); 277 278 // transform this equation: ymax = ?[val5] + ( tp_01y * resy ) 279 tp_02y = ymax + ( tp_01y * resy ); 280 281 double[] tiepoint = { tp_01x, tp_01y, tp_01z, tp_02x, tp_02y, tp_02z }; 282 283 // ModelTiepointTag: 284 // Tag = 33922 (8482.H) 285 // Type = DOUBLE (IEEE Double precision) 286 // N = 6*K, K = number of tiepoints 287 // Alias: GeoreferenceTag 288 // Owner: Intergraph 289 TIFFField modelTiepointTag = new TIFFField( GeoTiffTag.ModelTiepointTag, TIFFField.TIFF_DOUBLE, 6, tiepoint ); 290 291 this.tiffields.add( modelTiepointTag ); 292 } 293 294 // ************************************************************************ 295 // CoordinateSystem 296 // ************************************************************************ 297 /** 298 * 299 */ 300 private void setCoordinateSystem( CoordinateSystem crs ) { 301 org.deegree.crs.coordinatesystems.CoordinateSystem crs2 = crs.getCRS(); 302 if ( crs2 != null ) { 303 String[] identifiers = crs2.getIdentifiers(); 304 int epsg = -1; 305 for ( String id : identifiers ) { 306 LOG.logDebug( "trying to find EPSG code: " + id ); 307 if ( id.startsWith( "EPSG:" ) ) { 308 try { 309 epsg = Integer.parseInt( id.substring( 5 ) ); 310 break; 311 } catch ( NumberFormatException e ) { 312 // ignore and just try next one 313 } 314 } 315 } 316 if ( epsg != -1 ) { 317 int[] keyEntry = new int[] { 0, 1, epsg }; 318 if ( crs2.getType() == org.deegree.crs.coordinatesystems.CoordinateSystem.GEOGRAPHIC_CRS ) { 319 addKeyToGeoKeyDirectoryTag( GeoTiffKey.GTModelTypeGeoKey, 320 new int[] { 0, 1, ValueModelTypeGeographic } ); 321 addKeyToGeoKeyDirectoryTag( GeoTiffKey.GeographicTypeGeoKey, keyEntry ); 322 } else if ( crs2.getType() == org.deegree.crs.coordinatesystems.CoordinateSystem.PROJECTED_CRS ) { 323 addKeyToGeoKeyDirectoryTag( GeoTiffKey.GTModelTypeGeoKey, 324 new int[] { 0, 1, ValueModelTypeProjected } ); 325 addKeyToGeoKeyDirectoryTag( GeoTiffKey.ProjectedCSTypeGeoKey, keyEntry ); 326 } else { 327 LOG.logWarning( "can't save coordinate system. coordinate type " + crs2.getType() 328 + " not supported" ); 329 } 330 } 331 } 332 } 333 334 }