/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2012, 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.referencing.factory.gridshift;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.factory.AbstractFactory;
import org.geotools.factory.BufferedFactory;
import org.geotools.referencing.factory.ReferencingFactory;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.geotools.util.SoftValueHashMap;
import org.geotools.util.logging.Logging;
import org.opengis.referencing.FactoryException;
import au.com.objectix.jgridshift.GridShiftFile;
/**
* Loads and caches NTv2 grid files. Thisthat incorporates a soft cache mechanism to keep grids in
* memory when first loaded. It also checks NTv2 grid file format in {@link #isNTv2Grid(String)}
* method.
*
* @author Oscar Fonts
*/
public class NTv2GridShiftFactory extends ReferencingFactory implements BufferedFactory {
/**
* The number of hard references to hold internally.
*/
private static final int GRID_CACHE_HARD_REFERENCES = 10;
/**
* Logger.
*/
protected static final Logger LOGGER = Logging.getLogger("org.geotools.referencing");
/**
* The soft cache that holds loaded grids.
*/
private SoftValueHashMap<String, GridShiftFile> ntv2GridCache;
/**
* Constructs a factory with the default priority.
*/
public NTv2GridShiftFactory() {
super();
ntv2GridCache = new SoftValueHashMap<String, GridShiftFile>(GRID_CACHE_HARD_REFERENCES);
}
/**
* Constructs an instance using the specified priority level.
*
* @param priority The priority for this factory, as a number between
* {@link AbstractFactory#MINIMUM_PRIORITY MINIMUM_PRIORITY} and
* {@link AbstractFactory#MAXIMUM_PRIORITY MAXIMUM_PRIORITY} inclusive.
*/
public NTv2GridShiftFactory(final int priority) {
super(priority);
ntv2GridCache = new SoftValueHashMap<String, GridShiftFile>(GRID_CACHE_HARD_REFERENCES);
}
/**
* Performs a NTv2 grid file lookup given its name, and checks for file format correctness.
*
* @param name The NTv2 grid file name
* @return {@code true} if file exists and is valid, {@code false} otherwise
*/
public boolean isNTv2Grid(URL location) {
if (location != null) {
return isNTv2GridFileValid(location); // Check
} else {
return false;
}
}
/**
* Creates a NTv2 Grid.
*
* @param name The NTv2 grid name
* @return the grid
* @throws FactoryException if grid cannot be created
*/
public GridShiftFile createNTv2Grid(URL gridLocation) throws FactoryException {
if(gridLocation == null) {
throw new FactoryException("The grid location must be not null");
}
synchronized (ntv2GridCache) { // Prevent simultaneous threads trying to load same grid
GridShiftFile grid = ntv2GridCache.get(gridLocation.toExternalForm());
if (grid != null) { // Cached:
return grid; // - Return
} else { // Not cached:
if (gridLocation != null) {
grid = loadNTv2Grid(gridLocation); // - Load
if (grid != null) {
ntv2GridCache.put(gridLocation.toExternalForm(), grid); // - Cache
return grid; // - Return
}
}
throw new FactoryException("NTv2 Grid " + gridLocation + " could not be created.");
}
}
}
/**
* Checks if a given resource is a valid NTv2 file without fully loading it.
*
* If file is not valid, the cause is logged at {@link Level#WARNING warning level}.
*
* @param location the NTv2 file absolute path
* @return true if file has NTv2 format, false otherwise
*/
protected boolean isNTv2GridFileValid(URL url) {
RandomAccessFile raf = null;
InputStream is = null;
try {
// Loading as RandomAccessFile doesn't load the full grid
// in memory, but is a quick method to see if file format
// is NTv2.
if (url.getProtocol().equals("file")) {
File file = DataUtilities.urlToFile(url);
if (!file.exists() || !file.canRead()) {
throw new IOException(Errors.format(ErrorKeys.FILE_DOES_NOT_EXIST_$1, file));
}
raf = new RandomAccessFile(file, "r");
// will throw an exception if not a valid file
new GridShiftFile().loadGridShiftFile(raf);
} else {
InputStream in = new BufferedInputStream(url.openConnection().getInputStream());
// will throw an exception if not a valid file
new GridShiftFile().loadGridShiftFile(in, false);
}
return true; // No exception thrown => valid file.
} catch (IllegalArgumentException e) {
// This usually means resource is not a valid NTv2 file.
// Let exception message describe the cause.
LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
return false;
} catch (IOException e) {
LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
return false;
} finally {
try {
if (raf != null)
raf.close();
} catch (IOException e) {
}
try {
if (is != null)
is.close();
} catch (IOException e) {
}
}
}
/**
* Loads the grid in memory.
*
* If file cannot be loaded, the cause is logged at {@link Level#SEVERE severe level}.
*
* @param location the NTv2 file absolute path
* @return the grid, or {@code null} on error
* @throws FactoryException
*/
private GridShiftFile loadNTv2Grid(URL location) throws FactoryException {
InputStream in = null;
try {
GridShiftFile grid = new GridShiftFile();
in = new BufferedInputStream(location.openStream());
grid.loadGridShiftFile(in, false); // Load full grid in memory
in.close();
return grid;
} catch (FileNotFoundException e) {
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
return null;
} catch (IOException e) {
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
return null;
} catch (IllegalArgumentException e) {
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
throw new FactoryException(e.getLocalizedMessage(), e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
// never mind
}
}
}
}