org.geotools.gce.imagepyramid.Utils.java Source code

Java tutorial

Introduction

Here is the source code for org.geotools.gce.imagepyramid.Utils.java

Source

/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2006-2008, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.gce.imagepyramid;

import java.awt.geom.AffineTransform;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.geotools.data.DataUtilities;
import org.geotools.factory.Hints;
import org.geotools.gce.imagemosaic.ImageMosaicFormat;
import org.geotools.gce.imagemosaic.ImageMosaicReader;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.resources.coverage.CoverageUtilities;
import org.geotools.util.logging.Logging;
import org.opengis.referencing.datum.PixelInCell;

/**
 * Code to build a pyramid from a gdal_retile output
 * 
 * @author Andrea Aime - GeoSolutions SAS
 * @author Simone Giannecchini, GeoSolutions SAS
 *
 */
class Utils {

    static final Logger LOGGER = Logging.getLogger(Utils.class);

    static URL checkSource(Object source) {
        return checkSource(source, null);
    }

    static URL checkSource(Object source, Hints hints) {
        URL sourceURL = null;
        File sourceFile = null;

        //
        // Check source
        //
        // if it is a URL or a String let's try to see if we can get a file to
        // check if we have to build the index
        if (source instanceof File) {
            sourceFile = (File) source;
            sourceURL = DataUtilities.fileToURL(sourceFile);
        } else if (source instanceof URL) {
            sourceURL = (URL) source;
            if (sourceURL.getProtocol().equals("file")) {
                sourceFile = DataUtilities.urlToFile(sourceURL);
            }
        } else if (source instanceof String) {
            // is it a File?
            final String tempSource = (String) source;
            File tempFile = new File(tempSource);
            if (!tempFile.exists()) {
                // is it a URL
                try {
                    sourceURL = new URL(tempSource);
                    source = DataUtilities.urlToFile(sourceURL);
                } catch (MalformedURLException e) {
                    sourceURL = null;
                    source = null;
                }
            } else {
                sourceURL = DataUtilities.fileToURL(tempFile);
                sourceFile = tempFile;
            }
        } else {
            // we really don't know how to convert the thing... give up
            if (LOGGER.isLoggable(Level.WARNING)) {
                LOGGER.warning("we really don't know how to convert the thing:" + source != null ? source.toString()
                        : "null");
            }
            return null;
        }

        // logging
        if (LOGGER.isLoggable(Level.INFO)) {
            if (sourceFile != null) {
                final String message = fileStatus(sourceFile);
                LOGGER.info(message);
            }
        }

        //
        // Handle cases where the pyramid descriptor file already exists
        //
        // can't do anything with it
        if (sourceFile == null || !sourceFile.exists())
            return sourceURL;

        // if it's already a file we don't need to adjust it, will try to open as is
        if (!sourceFile.isDirectory())
            return sourceURL;

        // it's a directory, let's see if it already has a pyramid description file inside
        File directory = sourceFile;
        sourceFile = new File(directory, directory.getName() + ".properties");
        // logging
        if (LOGGER.isLoggable(Level.INFO)) {
            if (sourceFile != null) {
                final String message = fileStatus(sourceFile);
                LOGGER.info(message);
            }
        }
        if (sourceFile.exists())
            return DataUtilities.fileToURL(sourceFile);

        //
        // Try to build the sub-folders mosaics
        //
        // if the structure of the directories is gdal_retile like, move the root files in their
        // own sub directory
        File zeroLevelDirectory = new File(directory, "0");
        IOFileFilter directoryFilter = FileFilterUtils.directoryFileFilter();
        File[] numericDirectories = directory.listFiles(new NumericDirectoryFilter());
        File[] directories = directory.listFiles((FileFilter) directoryFilter);

        // do we have at least one sub-directory?
        if (directories.length == 0) {
            if (LOGGER.isLoggable(Level.INFO)) {
                LOGGER.info("I was unable to determine a structure similar to the GDAL Retile one!!");
            }
            return null;
        }

        // check the gdal case and move files if necessary
        if (!zeroLevelDirectory.exists() && numericDirectories.length == directories.length) {
            LOGGER.log(Level.INFO,
                    "Detected gdal_retile file structure, " + "moving root files to the '0' subdirectory");
            if (zeroLevelDirectory.mkdir()) {
                if (LOGGER.isLoggable(Level.FINE)) {
                    LOGGER.fine("Created '0' subidr, now moving files");
                }
                FileFilter notDirFilter = FileFilterUtils.notFileFilter(directoryFilter);
                for (File f : directory.listFiles(notDirFilter)) {
                    if (LOGGER.isLoggable(Level.FINE)) {
                        LOGGER.fine("Moving file" + f.getAbsolutePath());
                    }
                    if (LOGGER.isLoggable(Level.FINEST)) {
                        LOGGER.finest(fileStatus(f));
                    }
                    if (!f.renameTo(new File(zeroLevelDirectory, f.getName())))
                        LOGGER.log(Level.WARNING,
                                "Could not move " + f.getAbsolutePath() + " to " + zeroLevelDirectory
                                        + " check the permission inside the source directory " + f.getParent()
                                        + " and target directory " + zeroLevelDirectory);
                }
                directories = directory.listFiles((FileFilter) directoryFilter);
            } else {
                if (LOGGER.isLoggable(Level.INFO)) {
                    LOGGER.info(
                            "I was unable to create the 0 directory. check the file permission in the parent directory:"
                                    + sourceFile.getParent());
                }
                return null;
            }
        }

        // scan each subdirectory and try to build a mosaic in it, accumulate the resulting mosaics
        List<MosaicInfo> mosaics = new ArrayList<MosaicInfo>();
        ImageMosaicFormat mosaicFactory = new ImageMosaicFormat();
        for (File subdir : directories) {
            if (mosaicFactory.accepts(subdir, hints)) {
                if (LOGGER.isLoggable(Level.FINE)) {
                    LOGGER.fine("Trying to build mosaic for the directory:" + subdir.getAbsolutePath());
                }
                mosaics.add(new MosaicInfo(subdir, mosaicFactory.getReader(subdir, hints)));
            } else {
                if (LOGGER.isLoggable(Level.INFO)) {
                    LOGGER.info("Unable to build mosaic for the directory:" + subdir.getAbsolutePath());
                }
            }
        }

        // do we have at least one level?
        if (mosaics.size() == 0)
            return null;

        // sort the mosaics by resolution and check they are actually in ascending resolution order
        // for both X and Y resolutions
        Collections.sort(mosaics);
        for (int i = 1; i < mosaics.size(); i++) {
            double[] resprev = mosaics.get(i - 1).getResolutions();
            double[] res = mosaics.get(i).getResolutions();
            if (resprev[1] > res[1]) {
                LOGGER.log(Level.INFO,
                        "Invalid mosaic, y resolution in " + mosaics.get(i - 1).getPath()
                                + " is greater than the one in " + mosaics.get(i).getPath()
                                + " whilst x resolutions " + "have the opposite relationship");
                return null;
            }
        }

        //
        // We have everything we need, build the final pyramid descriptor info
        //        
        // build the property file
        Properties properties = new Properties();
        properties.put("Name", directory.getName());
        properties.put("LevelsNum", String.valueOf(mosaics.size()));
        StringBuilder sbDirNames = new StringBuilder();
        StringBuilder sbLevels = new StringBuilder();
        for (MosaicInfo mi : mosaics) {
            sbDirNames.append(mi.getName()).append(" ");
            double[] resolutions = mi.getResolutions();
            sbLevels.append(resolutions[0]).append(",").append(resolutions[1]).append(" ");
        }
        properties.put("LevelsDirs", sbDirNames.toString());
        properties.put("Levels", sbLevels.toString());
        GeneralEnvelope envelope = mosaics.get(0).getEnvelope();
        properties.put("Envelope2D", envelope.getMinimum(0) + "," + envelope.getMinimum(1) + " "
                + envelope.getMaximum(0) + "," + envelope.getMaximum(1));
        OutputStream os = null;
        try {
            os = new FileOutputStream(sourceFile);
            properties.store(os, "Automatically generated");
        } catch (IOException e) {
            LOGGER.log(Level.INFO, "We could not generate the pyramid propert file " + sourceFile.getPath(), e);
            return null;
        } finally {
            if (os != null)
                IOUtils.closeQuietly(os);
        }

        // build the .prj file if possible
        if (envelope.getCoordinateReferenceSystem() != null) {
            File prjFile = new File(directory, directory.getName() + ".prj");
            PrintWriter pw = null;
            try {
                pw = new PrintWriter(new FileOutputStream(prjFile));
                pw.print(envelope.getCoordinateReferenceSystem().toString());
            } catch (IOException e) {
                LOGGER.log(Level.INFO, "We could not write out the projection file " + prjFile.getPath(), e);
                return null;
            } finally {
                pw.close();
            }
        }

        return DataUtilities.fileToURL(sourceFile);
    }

    /**
     * Prepares a message with the status of the provided file.
     * @param sourceFile The {@link File} to provided the status message for
     * @return a status message for the provided {@link File} or a {@link NullPointerException} in case the {@link File}is <code>null</code>.
     */
    private static String fileStatus(File sourceFile) {
        if (sourceFile == null) {
            throw new NullPointerException("Provided null input to fileStatus method");
        }
        final StringBuilder builder = new StringBuilder();
        builder.append("Checking file:").append(FilenameUtils.getFullPath(sourceFile.getAbsolutePath()))
                .append("\n");
        builder.append("exists").append(sourceFile.exists()).append("\n");
        builder.append("isFile").append(sourceFile.isFile()).append("\n");
        builder.append("canRead:").append(sourceFile.canRead()).append("\n");
        builder.append("canWrite").append(sourceFile.canWrite()).append("\n");
        builder.append("canExecute").append(sourceFile.canExecute()).append("\n");
        builder.append("isHidden:").append(sourceFile.isHidden()).append("\n");
        builder.append("lastModified").append(sourceFile.lastModified()).append("\n");

        return builder.toString();
    }

    /**
     * Stores informations about a mosaic 
     */
    static class MosaicInfo implements Comparable<MosaicInfo> {
        @Override
        public String toString() {
            return "MosaicInfo [directory=" + directory + ", resolutions=" + Arrays.toString(resolutions) + "]";
        }

        File directory;
        ImageMosaicReader reader;
        double[] resolutions;

        MosaicInfo(File directory, ImageMosaicReader reader) {
            this.directory = directory;
            this.reader = reader;
            this.resolutions = CoverageUtilities
                    .getResolution((AffineTransform) reader.getOriginalGridToWorld(PixelInCell.CELL_CORNER));
        }

        double[] getResolutions() {
            return resolutions;
        }

        String getPath() {
            return directory.getPath();
        }

        String getName() {
            return directory.getName();
        }

        GeneralEnvelope getEnvelope() {
            return reader.getOriginalEnvelope();
        }

        public int compareTo(MosaicInfo other) {
            // we make an easy comparison against the x resolution, we'll do a sanity
            // check about the y resolution later
            return resolutions[0] > other.resolutions[0] ? 1 : -1;
        }
    }

    /**
     * A file filter that only returns directories whose name is an integer number
     * @author Andrea Aime - OpenGeo
     */
    static class NumericDirectoryFilter implements FileFilter {

        public boolean accept(File pathname) {
            if (!pathname.isDirectory())
                return false;
            try {
                Integer.parseInt(pathname.getName());
                return true;
            } catch (NumberFormatException e) {
                return false;
            }
        }
    }

}