001    //$HeadURL: https://sushibar/svn/deegree/base/trunk/resources/eclipse/svn_classfile_header_template.xml $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2008 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014     This library is distributed in the hope that it will be useful,
015     but WITHOUT ANY WARRANTY; without even the implied warranty of
016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     Lesser General Public License for more details.
018     You should have received a copy of the GNU Lesser General Public
019     License along with this library; if not, write to the Free Software
020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     Contact:
022    
023     Andreas Poth
024     lat/lon GmbH
025     Aennchenstr. 19
026     53177 Bonn
027     Germany
028     E-Mail: poth@lat-lon.de
029    
030     Prof. Dr. Klaus Greve
031     Department of Geography
032     University of Bonn
033     Meckenheimer Allee 166
034     53115 Bonn
035     Germany
036     E-Mail: greve@giub.uni-bonn.de
037     ---------------------------------------------------------------------------*/
038    
039    package org.deegree.tools.raster;
040    
041    import java.awt.geom.AffineTransform;
042    import java.awt.geom.NoninvertibleTransformException;
043    import java.awt.image.BufferedImage;
044    import java.awt.image.ColorModel;
045    import java.awt.image.SampleModel;
046    import java.io.File;
047    import java.io.IOException;
048    import java.util.LinkedList;
049    
050    import javax.imageio.ImageIO;
051    
052    import org.deegree.framework.log.ILogger;
053    import org.deegree.framework.log.LoggerFactory;
054    import org.deegree.model.coverage.grid.WorldFile;
055    import org.deegree.model.spatialschema.Envelope;
056    import org.deegree.model.spatialschema.GeometryFactory;
057    import org.deegree.processing.raster.converter.Image2RawData;
058    import org.deegree.processing.raster.converter.RawData2Image;
059    
060    /**
061     * <code>WorldfileNormalizer</code>
062     * 
063     * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
064     * @author last edited by: $Author:$
065     * 
066     * @version $Revision:$, $Date:$
067     */
068    public class WorldfileNormalizer {
069    
070        private static ILogger LOG = LoggerFactory.getLogger( WorldfileNormalizer.class );
071    
072        private Config config;
073    
074        private double resx, resy, rotx, roty, minx, miny;
075    
076        // int xmin = Integer.MAX_VALUE;
077        //
078        // int ymin = Integer.MAX_VALUE;
079        //
080        // int xmax = Integer.MIN_VALUE;
081        //
082        // int ymax = Integer.MIN_VALUE;
083    
084        private int width, height;
085    
086        private AffineTransform transform = new AffineTransform();
087    
088        /**
089         * @param config
090         */
091        public WorldfileNormalizer( Config config ) {
092            this.config = config;
093        }
094    
095        // x -> y -> interpolation -> wert
096        private float getValue( Image2RawData data, double x, double y ) {
097            // transform = new AffineTransform(resx, roty, minx + x*resx, rotx, resy, miny + y*resy);
098    
099            // System.out.println( transform );
100            // try {
101            // transform = transform.createInverse();
102            // } catch ( NoninvertibleTransformException e ) {
103            // // TODO Auto-generated catch block
104            // e.printStackTrace();
105            // }
106            // int srcy = (int)( ( resx * ( y - miny ) - rotx * ( x - minx ) ) / denominator );
107            // int srcx = (int)( ( x - roty * srcy - minx ) / resx );
108            // int srcy = (int)( ( ( y ) - rotx * ( x ) ) / denominator );
109            // int srcx = (int)( ( x - roty * srcy ) );
110    
111            // double tmpx = (int) ( x * cos1 - y * sin1 );
112            // double tmpy = (int) ( x * sin1 + y * cos1 );
113            double[] dest = new double[2];
114            transform.transform( new double[] { x, y }, 0, dest, 0, 1 );
115            int srcx = (int) ( dest[0] );
116            int srcy = (int) ( dest[1] );
117            if ( srcx < width && srcx >= 0 && srcy >= 0 && srcy < height ) {
118                return data.get( srcx, srcy );
119            }
120    
121            // System.out.println( srcx + "/" + srcy );
122            return 0;
123        }
124    
125        // private int getTargetWidth() {
126        // double hyp = width / Math.cos( rotx );
127        // hyp *= hyp;
128        // double res = Math.sqrt( hyp - width * width );
129        // return (int) ( width + res );
130        // }
131        //
132        // private int getTargetHeight() {
133        // double hyp = height / Math.cos( rotx );
134        // hyp *= hyp;
135        // double res = Math.sqrt( hyp - height * height );
136        // return (int) ( height + res );
137        // }
138    
139        private void normalize( String file )
140                                throws IOException {
141            // avoid trying to read world files as images
142            if ( WorldFile.hasWorldfileSuffix( file ) ) {
143                return;
144            }
145            BufferedImage src = null;
146            try {
147                LOG.logInfo( "Reading " + file );
148                src = ImageIO.read( new File( file ) ); // ImageUtils.loadImage( file );
149                // src = ImageUtils.loadImage( file );
150            } catch ( Exception e ) {
151                LOG.logError( e.getMessage(), e );
152                LOG.logInfo( "Ignoring " + file );
153                // ignore faulty images/files that are no images
154                return;
155            }
156    
157            LOG.logInfo( "Read " + file );
158    
159            WorldFile wf = WorldFile.readWorldFile( file, config.type, src );
160    
161            Envelope env = wf.getEnvelope();
162    
163            file = file.substring( 0, file.length() - 4 );
164            resx = wf.getResx();
165            resy = -wf.getResy();
166            rotx = wf.getRotation1();
167            roty = wf.getRotation2();
168            minx = env.getMin().getX();
169            miny = env.getMax().getY();
170            double maxx = Double.MIN_VALUE, maxy = Double.MIN_VALUE;
171            minx = Double.MIN_VALUE; miny = Double.MIN_VALUE;
172            width = Math.abs( (int) ( wf.getEnvelope().getWidth() / resx ) ) + 1;
173            height = Math.abs( (int) ( wf.getEnvelope().getHeight() / resy ) ) + 1;
174    
175            transform = new AffineTransform( resx, rotx, roty, resy, minx, miny );
176            try {
177    
178                double[] d = new double[2];
179                for ( int x = 0; x < width; ++x )
180                    for ( int y = 0; y < height; ++y ) {
181    
182                        transform.transform( new double[] { x, y }, 0, d, 0, 1 );
183                        if ( d[0] > maxx )
184                            maxx = d[0];
185    
186                        if ( d[1] > maxy )
187                            maxy = d[1];
188                        
189                        if ( d[0] < minx )
190                            minx = d[0];
191    
192                        if ( d[1] < miny )
193                            miny = d[1];
194                    }
195    
196                transform = transform.createInverse();
197            } catch ( NoninvertibleTransformException e ) {
198                LOG.logError( "Worldfile was filled with invalid parameters.", e );
199            }
200    
201            int twidth = (int) ( ( maxx - minx ) / resx );
202            int theight = (int) -( ( maxy - miny ) / resy );
203    
204            LOG.logInfo( "Target image size is " + twidth + "x" + theight );
205           
206    
207            LOG.logInfo( "Image size is " + width + "x" + height );
208            Image2RawData srcData = new Image2RawData( src );
209    
210            ColorModel model = src.getColorModel();
211            SampleModel sampleModel = src.getSampleModel();
212    
213            float[][] destData = new float[theight][twidth];
214            for ( int i = 0; i < theight; ++i ) {
215                destData[i] = new float[twidth];
216            }
217    
218            LOG.logInfo( "Transforming image." );
219            for ( int x = 0; x < twidth; ++x ) {
220                if ( x % 1000 == 0 ) {
221                    System.out.print( "\r" + x );
222                }
223                for ( int y = 0; y < theight; ++y ) {
224                    destData[destData.length-y-1][x] = getValue( srcData, x * resx + minx, miny- y * resy  );
225                }
226            }
227    
228            System.out.println( "\r                  " );
229            LOG.logInfo( "Finished transforming image." );
230            srcData = null;
231            src = null;
232            System.gc();
233            System.gc();
234            // LOG.logInfo( "Target image size is " + twidth + "x" + theight );
235    
236            LOG.logInfo( "Creating target image..." );
237            BufferedImage dest = RawData2Image.rawData2Image( destData, true, model, sampleModel );
238            destData = null;
239            System.gc();
240            System.gc();
241    
242            LOG.logInfo( "Writing target image..." );
243            ImageIO.write( dest, "jpeg", new File( file + "_converted.jpg" ) );
244            LOG.logInfo( "Finished writing image." );
245    
246            double minx = env.getMin().getX();
247            double miny = env.getMin().getY();
248            Envelope env2 = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, env.getCoordinateSystem() );
249    
250            WorldFile outWF = new WorldFile( resx, resy, 0, 0, env2 );
251            WorldFile.writeWorldFile( outWF, file + "_converted" );
252        }
253    
254        private void normalize() {
255            while ( config.files.size() > 0 ) {
256                String file = config.files.poll();
257                try {
258                    normalize( file );
259                } catch ( IOException e ) {
260                    LOG.logWarning( "No world file named '" + file + "' could be found/read." );
261                    e.printStackTrace();
262                }
263            }
264        }
265    
266        /**
267         * @param args
268         */
269        public static void main( String[] args ) {
270            if ( args.length == 0 ) {
271                printUsage( null );
272            }
273    
274            WorldfileNormalizer wfn = new WorldfileNormalizer( new Config( args ) );
275            wfn.normalize();
276        }
277    
278        /**
279         * Prints usage information and exits.
280         * 
281         * @param msg
282         */
283        public static void printUsage( String msg ) {
284            if ( msg != null ) {
285                System.out.println( msg );
286                System.out.println();
287            }
288    
289            System.out.println( "Usage:" );
290            System.out.println( "java -cp ... " + WorldfileNormalizer.class.getCanonicalName() + " <options> <files>" );
291            System.out.println();
292            System.out.println( "      --help, -h:" );
293            System.out.println( "           Print out this message and exit." );
294            System.out.println( "      --directory, -d <dir>:" );
295            System.out.println( "           Adds all worldfiles in a directory to the list of files." );
296            System.out.println( "      --type, -t <type>:" );
297            System.out.println( "           Set the INPUT world file type, either to 'center' or to 'outer'." );
298            System.out.println( "           Note that the output world file type will always be 'center'." );
299            System.out.println( "           For details on the world file types see the documentation" );
300            System.out.println( "           of the RasterTreeBuilder." );
301            System.out.println( "      --interpolation, -i <method>:" );
302            System.out.println( "           Set the interpolation method. Can be one of 'bicubic', 'bilinear' or" );
303            System.out.println( "           'nearest'. Default is nearest neighbor." );
304    
305            System.exit( 0 );
306        }
307    
308        private static class Config {
309    
310            /**
311             * List of files to convert.
312             */
313            public LinkedList<String> files = new LinkedList<String>();
314    
315            /**
316             * The type of the world files.
317             */
318            public WorldFile.TYPE type = WorldFile.TYPE.CENTER;
319    
320            /**
321             * Interpolation method, default is 'nearest'.
322             */
323            public String interpolation = "nearest";
324    
325            /**
326             * Parses the commandline arguments. If -h or --help is contained in the array, the
327             * application will exit.
328             * 
329             * @param args
330             *            cmdline arguments
331             */
332            public Config( String[] args ) {
333                int i = 0;
334    
335                while ( i < args.length ) {
336                    if ( args[i].equals( "-h" ) || args[i].equals( "--help" ) ) {
337                        printUsage( null );
338                    } else if ( args[i].equals( "--type" ) || args[i].equals( "-t" ) ) {
339                        if ( args[i + 1].equalsIgnoreCase( "outer" ) ) {
340                            type = WorldFile.TYPE.OUTER;
341                        }
342                        if ( args[i + 1].equalsIgnoreCase( "center" ) ) {
343                            type = WorldFile.TYPE.CENTER;
344                        }
345                        i += 2;
346                    } else if ( args[i].equals( "--interpolation" ) || args[i].equals( "-i" ) ) {
347                        String m = args[i + 1].toLowerCase();
348                        if ( m.equals( "nearest" ) || m.equals( "bilinear" ) || m.equals( "biqubic" ) ) {
349                            interpolation = m;
350                        } else {
351                            printUsage( "Unknown interpolation method: '" + m + "'" );
352                        }
353                        i += 2;
354                    } else if ( args[i].equals( "-d" ) || args[i].equals( "--directory" ) ) {
355                        File dir = new File( args[i + 1] );
356                        File[] fs = dir.listFiles();
357                        for ( File f : fs ) {
358                            String s = f.getAbsolutePath();
359                            if ( s.toLowerCase().endsWith( "jpg" ) || s.toLowerCase().endsWith( "jpeg" )
360                                 || s.toLowerCase().endsWith( "gif" ) || s.toLowerCase().endsWith( "png" )
361                                 || s.toLowerCase().endsWith( "tif" ) || s.toLowerCase().endsWith( "tiff" )
362                                 || s.toLowerCase().endsWith( "bmp" ) ) {
363                                files.add( s );
364                            }
365                        }
366                        i += 2;
367                    } else {
368                        files.add( args[i] );
369                        ++i;
370                    }
371                }
372            }
373    
374        }
375    
376    }