|
From: <svn...@os...> - 2011-12-18 23:24:48
|
Author: simonegiannecchini
Date: 2011-12-18 15:24:41 -0800 (Sun, 18 Dec 2011)
New Revision: 38426
Modified:
trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/ImagePyramidFormat.java
trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/ImagePyramidReader.java
trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/Utils.java
Log:
minor improvements to messaging and mosaic caching
Modified: trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/ImagePyramidFormat.java
===================================================================
--- trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/ImagePyramidFormat.java 2011-12-18 23:23:59 UTC (rev 38425)
+++ trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/ImagePyramidFormat.java 2011-12-18 23:24:41 UTC (rev 38426)
@@ -55,7 +55,7 @@
/** Logger. */
private final static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.gce.imagepyramid");
- /**
+ /**
* Creates an instance and sets the metadata.
*/
public ImagePyramidFormat() {
@@ -124,28 +124,36 @@
*/
@Override
public boolean accepts(Object source, Hints hints) {
- try {
+ if(source==null){
+ throw new NullPointerException("Null parameter provided to the accepts method of this ImagePyramidFormat");
+ }
+
+ try {
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine("Accepts method of ImagePyramid with source" + source);
+ }
- // /////////////////////////////////////////////////////////////////////
//
// Check source
//
- // /////////////////////////////////////////////////////////////////////
- URL sourceURL = Utils.checkSource(source, hints);
+ URL sourceURL = Utils.checkSource(source, hints);
if(sourceURL == null)
return false;
try {
sourceURL.openStream().close();
} catch (Throwable e) {
+ if(LOGGER.isLoggable(Level.FINE)){
+ LOGGER.log(Level.FINE,e.getLocalizedMessage(),e);
+ }
return false;
}
- // ///////////////////////////////////////////////////////////////////
+
//
// Trying to load informations
//
//
- // ///////////////////////////////////////////////////////////////////
+
// //
//
// get the crs if able to
@@ -162,15 +170,15 @@
if (tempcrs == null) {
// use the default crs
tempcrs = AbstractGridFormat.getDefaultCRS();
- LOGGER.log(Level.FINE,new StringBuffer("Unable to find a CRS for this coverage, using a default one: ").append(tempcrs.toWKT()).toString());
+ LOGGER.log(Level.FINE,new StringBuilder("Unable to find a CRS for this coverage, using a default one: ").append(tempcrs.toWKT()).toString());
}
//
- // ///////////////////////////////////////////////////////////////////
+
//
// Load properties file with information about levels and envelope
//
//
- // ///////////////////////////////////////////////////////////////////
+
// property file
final Properties properties = new Properties();
BufferedInputStream propertyStream = null;
Modified: trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/ImagePyramidReader.java
===================================================================
--- trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/ImagePyramidReader.java 2011-12-18 23:23:59 UTC (rev 38425)
+++ trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/ImagePyramidReader.java 2011-12-18 23:24:41 UTC (rev 38426)
@@ -24,9 +24,9 @@
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.channels.Channels;
-import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -49,7 +49,6 @@
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.builder.GridToEnvelopeMapper;
-import org.geotools.util.SoftValueHashMap;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridCoverageReader;
@@ -141,8 +140,8 @@
/**
* Cache of {@link ImageMosaicReader} objects for the different levels.
*
- */
- private Map<Integer, ImageMosaicReader> readers;
+ */
+ private ConcurrentHashMap<Integer, ImageMosaicReader> readers =new ConcurrentHashMap<Integer, ImageMosaicReader>();
/**
* Constructor for an {@link ImagePyramidReader}.
@@ -175,23 +174,16 @@
// //
if (source == null) {
- final NullPointerException ex = new NullPointerException("ImagePyramidReader:No source set to read this coverage.");
- throw new DataSourceException(ex);
+ throw new DataSourceException("ImagePyramidReader:null source set to read this coverage.");
}
this.source = source;
this.sourceURL = Utils.checkSource(source, uHints);
if(sourceURL == null) {
- throw new IllegalArgumentException(
- "This plugin accepts only File, URL and String pointing to a file");
+ throw new IllegalArgumentException("This plugin accepts only File, URL and String pointing to a file");
}
// //
- //
- // Load tiles informations, especially the bounds, which will be
- // reused
- //
// //
- // //
//
// get the crs if able to
//
@@ -228,8 +220,6 @@
//
// Load properties file with information about levels and envelope
//
-
- // property file
parseMainFile(sourceURL);
}
@@ -242,6 +232,10 @@
* @throws FileNotFoundException
*/
private void parseMainFile(final URL sourceURL) throws IOException {
+
+ if(LOGGER.isLoggable(Level.FINE)){
+ LOGGER.fine("Parsing pyramid properties file at:"+sourceURL.toExternalForm());
+ }
BufferedInputStream propertyStream = null;
InputStream openStream = null;
try {
@@ -267,10 +261,6 @@
numOverviews = Integer.parseInt(properties.getProperty("LevelsNum")) - 1;
levelsDirs = properties.getProperty("LevelsDirs").split(" ");
- // readers soft map
- final int readersCacheSize = (numOverviews + 1) / 3;
- readers = new SoftValueHashMap<Integer, ImageMosaicReader>(readersCacheSize == 0 ? numOverviews + 1 : readersCacheSize);
-
// resolutions levels
final String levels = properties.getProperty("Levels");
pairs = levels.split(" ");
@@ -297,7 +287,11 @@
);
final GridToEnvelopeMapper geMapper= new GridToEnvelopeMapper(originalGridRange,originalEnvelope);
geMapper.setPixelAnchor(PixelInCell.CELL_CORNER);
- raster2Model= geMapper.createTransform();
+ raster2Model= geMapper.createTransform();
+
+ if(LOGGER.isLoggable(Level.FINE)){
+ LOGGER.fine("Parsed pyramid properties file at:"+sourceURL.toExternalForm());
+ }
} finally {
// close input stream
if (propertyStream != null)
@@ -369,11 +363,10 @@
}
}
}
- // /////////////////////////////////////////////////////////////////////
+
//
// Loading tiles
//
- // /////////////////////////////////////////////////////////////////////
return loadTiles(requestedEnvelope, dim, params, overviewPolicy);
}
@@ -393,7 +386,7 @@
Rectangle dim, GeneralParameterValue[] params, OverviewPolicy overviewPolicy)
throws IOException {
- // /////////////////////////////////////////////////////////////////////
+
//
// Check if we have something to load by intersecting the requested
// envelope with the bounds of the data set.
@@ -402,7 +395,7 @@
// we have to perform a conversion towards the latter crs before
// intersecting anything.
//
- // /////////////////////////////////////////////////////////////////////
+
if (requestedEnvelope != null) {
if (!CRS.equalsIgnoreMetadata(requestedEnvelope
.getCoordinateReferenceSystem(), this.crs)) {
@@ -470,37 +463,56 @@
throws TransformException, IOException {
// if we get here we have something to load
- // /////////////////////////////////////////////////////////////////////
+
//
// compute the requested resolution
//
- // /////////////////////////////////////////////////////////////////////
final ImageReadParam readP = new ImageReadParam();
Integer imageChoice=0;
if (dim != null)
imageChoice = setReadParams(overviewPolicy, readP,requestedEnvelope, dim);
- // /////////////////////////////////////////////////////////////////////
+
//
// Check to have the needed reader in memory
//
- // /////////////////////////////////////////////////////////////////////
- ImageMosaicReader reader = null;
- synchronized (readers) {
- Object o = readers.get(imageChoice);
- if (o == null) {
- final String levelDirName = levelsDirs[imageChoice.intValue()];
- final URL parentUrl = DataUtilities.getParentUrl(sourceURL);
- // look for a shapefile first
- final String extension = new StringBuilder(levelDirName).append("/").append(coverageName).append(".shp").toString();
- final URL shpFileUrl = DataUtilities.extendURL(parentUrl,extension);
- if(shpFileUrl.getProtocol()!=null&&shpFileUrl.getProtocol().equalsIgnoreCase("file")&&!DataUtilities.urlToFile(shpFileUrl).exists())
- reader= new ImageMosaicReader(DataUtilities.extendURL(parentUrl,levelDirName), hints);
- else
- reader = new ImageMosaicReader(shpFileUrl,hints);
- readers.put(imageChoice, reader);
-
- } else
- reader = (ImageMosaicReader) o;
+
+ // light check to see if this reader had been disposed, not synching for performance.
+ if (readers == null) {
+ throw new IllegalStateException("This ImagePyramidReader has already been disposed");
+ }
+
+ ImageMosaicReader reader = readers.get(imageChoice);
+ if (reader == null) {
+
+ //
+ // we must create the underlying mosaic
+ //
+ final String levelDirName = levelsDirs[imageChoice.intValue()];
+ final URL parentUrl = DataUtilities.getParentUrl(sourceURL);
+ // look for a shapefile first
+ final String extension = new StringBuilder(levelDirName).append("/").append(coverageName).append(".shp").toString();
+ final URL shpFileUrl = DataUtilities.extendURL(parentUrl, extension);
+ if (shpFileUrl.getProtocol() != null
+ && shpFileUrl.getProtocol().equalsIgnoreCase("file")
+ && !DataUtilities.urlToFile(shpFileUrl).exists())
+ reader = new ImageMosaicReader(DataUtilities.extendURL(parentUrl, levelDirName), hints);
+ else
+ reader = new ImageMosaicReader(shpFileUrl, hints);
+ final ImageMosaicReader putByOtherThreadJustNow =readers.putIfAbsent(imageChoice, reader);
+ if(putByOtherThreadJustNow!=null){
+ // some other thread just did inserted this
+ try{
+ reader.dispose();
+ } catch (Exception e) {
+ if(LOGGER.isLoggable(Level.FINE)){
+ LOGGER.log(Level.FINE,e.getLocalizedMessage(),e);
+ }
+ }
+
+ //use the other one
+ reader=putByOtherThreadJustNow;
+ }
+
}
Modified: trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/Utils.java
===================================================================
--- trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/Utils.java 2011-12-18 23:23:59 UTC (rev 38425)
+++ trunk/modules/plugin/imagepyramid/src/main/java/org/geotools/gce/imagepyramid/Utils.java 2011-12-18 23:24:41 UTC (rev 38426)
@@ -1,291 +1,356 @@
-/*
- * 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.Collections;
-import java.util.List;
-import java.util.Properties;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-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 - OpenGeo
- *
- */
-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
- return null;
- }
-
- // /////////////////////////////////////////////////////////////////////
- //
- // 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");
- 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)
- 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()) {
- FileFilter notDirFilter = FileFilterUtils.notFileFilter(directoryFilter);
- for (File f : directory.listFiles(notDirFilter)) {
- if(!f.renameTo(new File(zeroLevelDirectory, f.getName())))
- LOGGER.log(Level.INFO, "Could not move " + f.getAbsolutePath() +
- " to " + zeroLevelDirectory);
- }
- directories = directory.listFiles((FileFilter) directoryFilter);
- }
- }
-
- // 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)) {
- mosaics.add(new MosaicInfo(subdir, mosaicFactory.getReader(subdir, hints)));
- }
- }
-
- // 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)
- try { os.close(); } catch(IOException e) {}
- }
-
- // 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);
- }
-
-
- /**
- * Stores informations about a mosaic
- */
- static class MosaicInfo implements Comparable<MosaicInfo>{
- 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;
- }
- }
- }
-
-}
+/*
+ * 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);
+ }
+ 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;
+ }
+ }
+ }
+
+}
|