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    }