001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/model/coverage/grid/WorldFile.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    package org.deegree.model.coverage.grid;
037    
038    import java.awt.image.BufferedImage;
039    import java.io.BufferedReader;
040    import java.io.File;
041    import java.io.FileInputStream;
042    import java.io.FileWriter;
043    import java.io.IOException;
044    import java.io.InputStream;
045    import java.io.InputStreamReader;
046    import java.io.OutputStream;
047    import java.io.PrintWriter;
048    
049    import javax.media.jai.JAI;
050    import javax.media.jai.RenderedOp;
051    
052    import org.deegree.framework.log.ILogger;
053    import org.deegree.framework.log.LoggerFactory;
054    import org.deegree.model.spatialschema.Envelope;
055    import org.deegree.model.spatialschema.GeometryFactory;
056    
057    import com.sun.media.jai.codec.FileSeekableStream;
058    
059    /**
060     * class representation of a ESRI world file. A world file may defines bounding coordinates centered on the outter pixel
061     * (e.g. ESRI software) or outside the bounding pixels (e.g.Oracle spatial). Reading a worldfile this must be considered
062     * so the type of a worldfile must be passed. For this a <code>enum</code> named <code>TYPE</code> ist defined.
063     *
064     *
065     * @version $Revision: 18195 $
066     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
067     * @author last edited by: $Author: mschneider $
068     *
069     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
070     */
071    public class WorldFile {
072    
073        private static ILogger LOG = LoggerFactory.getLogger( WorldFile.class );
074    
075        private double resx;
076    
077        private double resy;
078    
079        private double rotation1;
080    
081        private double rotation2;
082    
083        private Envelope envelope;
084    
085        /**
086         * <code>TYPE</code> enumerates the world file types.
087         *
088         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
089         * @author last edited by: $Author: mschneider $
090         *
091         * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
092         */
093        public enum TYPE {
094    
095            /**
096             * Coordinates denote pixel centers.
097             */
098            CENTER,
099    
100            /**
101             * Coordinates denote outer edges.
102             */
103            OUTER
104    
105        }
106    
107        /**
108         * @return a class represention of a ESRI world file
109         * @param filename
110         *            name of the image/raster file inclusing path and extension
111         * @param type
112         * @throws IOException
113         */
114        public static WorldFile readWorldFile( String filename, TYPE type )
115                                throws IOException {
116    
117            FileSeekableStream fss = new FileSeekableStream( filename );
118            RenderedOp rop = JAI.create( "stream", fss );
119            int iw = ( (Integer) rop.getProperty( "image_width" ) ).intValue();
120            int ih = ( (Integer) rop.getProperty( "image_height" ) ).intValue();
121    
122            fss.close();
123    
124            return readWorldFile( filename, type, iw, ih );
125    
126        }
127    
128        /**
129         * @param name
130         * @return true, if the name ends with .tfw, .wld, .jgw, .gfw, .gifw, .pgw or .pngw.
131         */
132        public static boolean hasWorldfileSuffix( String name ) {
133            String lname = name.toLowerCase();
134            return lname.endsWith( ".tfw" ) || lname.endsWith( ".wld" ) || lname.endsWith( ".jgw" )
135                   || lname.endsWith( ".gfw" ) || lname.endsWith( ".gifw" ) || lname.endsWith( ".pgw" )
136                   || lname.endsWith( ".pngw" );
137        }
138    
139        /**
140         * @return a class representation of a ESRI world file
141         * @param is
142         * @param type
143         * @param width
144         *            image width in pixel
145         * @param height
146         *            image height in pixel
147         * @throws IOException
148         * @throws NumberFormatException
149         */
150        public static WorldFile readWorldFile( InputStream is, TYPE type, int width, int height )
151                                throws NumberFormatException, IOException {
152            BufferedReader br = new BufferedReader( new InputStreamReader( is ) );
153            String s = null;
154            int cnt = 0;
155            double d1 = 0;
156            double d2 = 0;
157            double d3 = 0;
158            double d4 = 0;
159            double d7 = 0;
160            double d8 = 0;
161            while ( ( s = br.readLine() ) != null ) {
162                cnt++;
163                s = s.trim();
164                switch ( cnt ) {
165                case 1:
166                    // spatial resolution x direction
167                    d1 = Double.parseDouble( s.replace( ',', '.' ) );
168                    break;
169                case 2:
170                    // rotation1
171                    d7 = Double.parseDouble( s.replace( ',', '.' ) );
172                    break;
173                case 3:
174                    // rotation2
175                    d8 = Double.parseDouble( s.replace( ',', '.' ) );
176                    break;
177                case 4:
178                    // spatial resolution y direction
179                    d2 = Double.parseDouble( s.replace( ',', '.' ) );
180                    break;
181                case 5:
182                    // minimum x coordinate
183                    d3 = Double.parseDouble( s.replace( ',', '.' ) );
184                    break;
185                case 6:
186                    // maximum y coordinate
187                    d4 = Double.parseDouble( s.replace( ',', '.' ) );
188                    break;
189                }
190            }
191            br.close();
192    
193            double d5 = d3 + ( ( width - 1 ) * d1 );
194            double d6 = d4 + ( ( height - 1 ) * d2 );
195            double resx = Math.abs( d1 );
196            double resy = Math.abs( d2 );
197            double ymax = d4;
198            double ymin = d6;
199            double xmax = d5;
200            double xmin = d3;
201    
202            if ( type == TYPE.OUTER ) {
203                LOG.logDebug( xmin + " " + ymin + " " + xmax + " " + ymax );
204                xmin = xmin + resx / 2d;
205                ymin = ymin - resy / 2d;
206                xmax = xmin + resx * ( width - 1 );
207                ymax = ymin + resy * ( height - 1 );
208            }
209    
210            Envelope envelope = GeometryFactory.createEnvelope( xmin, ymin, xmax, ymax, null );
211    
212            return new WorldFile( resx, resy, d7, d8, envelope );
213        }
214    
215        /**
216         * @return a class representation of a ESRI world file
217         * @param filename
218         *            name of the image/raster file inclusing path and extension
219         * @param type
220         * @param width
221         *            image width in pixel
222         * @param height
223         *            image height in pixel
224         * @throws IOException
225         */
226        public static WorldFile readWorldFile( String filename, TYPE type, int width, int height )
227                                throws IOException {
228            // Gets the substring beginning at the specified beginIndex (0) - the beginning index,
229            // inclusive - and extends to the character at index endIndex (position of '.') - the
230            // ending index, exclusive.
231    
232            String fname = null;
233            int pos = filename.lastIndexOf( "." );
234            filename = filename.substring( 0, pos );
235    
236            // Look for corresponding world files.
237            if ( ( new File( filename + ".tfw" ) ).exists() ) {
238                fname = filename + ".tfw";
239            } else if ( ( new File( filename + ".wld" ) ).exists() ) {
240                fname = filename + ".wld";
241            } else if ( ( new File( filename + ".jgw" ) ).exists() ) {
242                fname = filename + ".jgw";
243            } else if ( ( new File( filename + ".jpgw" ) ).exists() ) {
244                fname = filename + ".jpgw";
245            } else if ( ( new File( filename + ".gfw" ) ).exists() ) {
246                fname = filename + ".gfw";
247            } else if ( ( new File( filename + ".gifw" ) ).exists() ) {
248                fname = filename + ".gifw";
249            } else if ( ( new File( filename + ".pgw" ) ).exists() ) {
250                fname = filename + ".pgw";
251            } else if ( ( new File( filename + ".pngw" ) ).exists() ) {
252                fname = filename + ".pngw";
253            } else {
254                throw new IOException( "Not a world file for: " + filename );
255            }
256    
257            // Reads character files.
258            // The constructors of this class (FileReader) assume that the default character
259            // encoding and the default byte-buffer size are appropriate.
260            // The BufferedReader reads text from a character-input stream, buffering characters
261            // so as to provide for the efficient reading of characters.
262            return readWorldFile( new FileInputStream( fname ), type, width, height );
263    
264        }
265    
266        /**
267         * returns a class representation of a ESRI world file
268         *
269         * @param filename
270         *            name of the image/raster file including path and extension
271         * @param type
272         *            world file type
273         * @param image
274         *            image/raster the world file belongs too
275         * @return a class representation of a ESRI world file
276         * @throws IOException
277         */
278        public static WorldFile readWorldFile( String filename, TYPE type, BufferedImage image )
279                                throws IOException {
280    
281            return readWorldFile( filename, type, image.getWidth(), image.getHeight() );
282        }
283    
284        /**
285         * writes a WorldFile
286         *
287         * @param wf
288         * @param fileBaseName
289         * @throws IOException
290         */
291        public static void writeWorldFile( WorldFile wf, String fileBaseName )
292                                throws IOException {
293    
294            Envelope env = wf.envelope;
295    
296            StringBuffer sb = new StringBuffer( 200 );
297    
298            sb.append( wf.resx ).append( "\n" ).append( 0.0 ).append( "\n" ).append( 0.0 );
299            sb.append( "\n" ).append( ( -1 ) * wf.resy ).append( "\n" ).append( env.getMin().getX() );
300            sb.append( "\n" ).append( env.getMax().getY() ).append( "\n" );
301    
302            File f = new File( fileBaseName + ".wld" );
303    
304            FileWriter fw = new FileWriter( f );
305            PrintWriter pw = new PrintWriter( fw );
306    
307            pw.print( sb.toString() );
308    
309            pw.close();
310            fw.close();
311        }
312    
313        /**
314         * writes a WorldFile
315         *
316         * @param os
317         * @param wf
318         * @throws IOException
319         */
320        public static void writeWorldFile( OutputStream os, WorldFile wf )
321                                throws IOException {
322    
323            Envelope env = wf.envelope;
324    
325            StringBuffer sb = new StringBuffer( 200 );
326    
327            sb.append( wf.resx ).append( "\n" ).append( 0.0 ).append( "\n" ).append( 0.0 );
328            sb.append( "\n" ).append( ( -1 ) * wf.resy ).append( "\n" ).append( env.getMin().getX() );
329            sb.append( "\n" ).append( env.getMax().getY() ).append( "\n" );
330    
331            PrintWriter pw = new PrintWriter( os );
332            pw.print( sb.toString() );
333            pw.close();
334        }
335    
336        /**
337         * Create a new WorldFile with an envelope that spans from the center of the corner pixels.
338         *
339         * @param resx
340         *            resolution x-direction
341         * @param resy
342         *            resolution y-direction (negative value)
343         * @param rotation1
344         *            first rotation parameter
345         * @param rotation2
346         *            second rotation parameter
347         * @param envelope
348         *            the envelope of the worldfile
349         */
350        public WorldFile( double resx, double resy, double rotation1, double rotation2, Envelope envelope ) {
351            this.resx = resx;
352            this.resy = resy;
353            this.rotation1 = rotation1;
354            this.rotation2 = rotation2;
355            this.envelope = envelope;
356        }
357    
358        /**
359         * Create a new WorldFile with an envelope.
360         *
361         * @param resx
362         *            resolution x-direction
363         * @param resy
364         *            resolution y-direction (negative value)
365         * @param rotation1
366         *            first rotation parameter
367         * @param rotation2
368         *            second rotation parameter
369         * @param envelope
370         *            the envelope of the worldfile
371         * @param type
372         *            whether the envelope spans from the center or from the outer bounds of the corner pixels
373         */
374        public WorldFile( double resx, double resy, double rotation1, double rotation2, Envelope envelope, TYPE type ) {
375            this.resx = resx;
376            this.resy = resy;
377            this.rotation1 = rotation1;
378            this.rotation2 = rotation2;
379            if ( type == TYPE.CENTER ) {
380                this.envelope = envelope;
381            } else { // convert to internal TYPE.CENTER format
382                this.envelope = GeometryFactory.createEnvelope( envelope.getMin().getX() + resx / 2,
383                                                                envelope.getMin().getY() + resy / 2,
384                                                                envelope.getMax().getX() - resx / 2,
385                                                                envelope.getMax().getY() - resy / 2,
386                                                                envelope.getCoordinateSystem() );
387            }
388        }
389    
390        /**
391         * returns the envelope described by a word file. The envelope spans the center coordinates of the corner pixels.
392         *
393         * @return the envelope described by a word file
394         */
395        public Envelope getEnvelope() {
396            return envelope;
397        }
398    
399        /**
400         * returns the envelope described by a word file
401         *
402         * @param envType
403         *            whether the result envelope should span from the center or from the outer bounds of the corner pixels
404         * @return the envelope described by a word file
405         */
406        public Envelope getEnvelope( TYPE envType ) {
407            if ( envType == TYPE.CENTER ) {
408                return envelope;
409            }
410            // convert from internal TYPE.CENTER format to TYPE.OUTER
411            return GeometryFactory.createEnvelope( envelope.getMin().getX() - resx / 2,
412                                                   envelope.getMin().getY() - resy / 2,
413                                                   envelope.getMax().getX() + resx / 2,
414                                                   envelope.getMax().getY() + resy / 2, envelope.getCoordinateSystem() );
415        }
416    
417        /**
418         * returns the x-resolution described by a word file
419         *
420         * @return the x-resolution described by a word file
421         */
422        public double getResx() {
423            return resx;
424        }
425    
426        /**
427         * returns the y-resolution described by a word file
428         *
429         * @return the y-resolution described by a word file
430         */
431        public double getResy() {
432            return resy;
433        }
434    
435        /**
436         * returns the first rotation described by a word file
437         *
438         * @return the first rotation described by a word file
439         */
440        public double getRotation1() {
441            return rotation1;
442        }
443    
444        /**
445         * returns the second rotation described by a word file
446         *
447         * @return the second rotation described by a word file
448         */
449        public double getRotation2() {
450            return rotation2;
451        }
452    
453        @Override
454        public String toString() {
455            StringBuffer sb = new StringBuffer( 200 );
456            sb.append( "envelope: " ).append( envelope ).append( "\n" );
457            sb.append( "resx: " ).append( resx ).append( "\n" );
458            sb.append( "resy: " ).append( resy ).append( "\n" );
459            sb.append( "rotation1: " ).append( rotation1 ).append( "\n" );
460            sb.append( "rotation2: " ).append( rotation2 );
461            return sb.toString();
462        }
463    
464    }