    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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    }