001 //$HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/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 (Thu, 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 }