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