/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2013 - 2016, 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.coverage.grid.io.footprint; import java.io.File; import java.io.IOException; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.io.FilenameUtils; import org.geotools.util.logging.Logging; import org.opengis.feature.simple.SimpleFeature; import com.vividsolutions.jts.geom.Geometry; /** * A footprint provider looking for sidecar files (SHP, WKB, WKT, ...). * By default, footprints are searched as files living beside the data file. * In case a "FOOTPRINTS_DATA_DIR" property is specified, footprints are * searched into an external directory too in case they aren't found on the main * folder. * * This can be useful for cases where the data file lives into a read only folder. * * Suppose data is in /path/to/mydata/tile.tif * In the need of supporting footprints into a different location, * users should replicate that path within a common folder and define that common folder * through the "FOOTPRINTS_DATA_DIR" system property. * * As an instance, users may put a tile.wkb into * /footprints/path/to/mydata/tile.wkb * having specified -DFOOTPRINTS_DATA_DIR=/footprints at startup. * * @author Andrea Aime - GeoSolutions * @author Daniele Romagnoli - GeoSolutions * * @see MultiLevelROIProviderFactory#FOOTPRINTS_DATA_DIR_KEY */ public class SidecarFootprintProvider implements FootprintGeometryProvider { static final Logger LOGGER = Logging.getLogger(SidecarFootprintProvider.class); static final Set<FootprintLoader> LOADERS = new HashSet<FootprintLoader>(); private static final String FOOTPRINT_LOCATION_ATTRIBUTE = "location"; /** String associated to the footprints data directory property */ public static final String FOOTPRINTS_DATA_DIR_KEY = "FOOTPRINTS_DATA_DIR"; /** The footprints data directory (when specified) */ private static final String FOOTPRINTS_DATA_DIR; /** * Static initialization, FOOTPRINTS_DATA_DIR if set as JAVA argument */ static { final Object prefixDir = System.getProperty(FOOTPRINTS_DATA_DIR_KEY); String footprintsDir = null; if (prefixDir != null) { String dir = (String) prefixDir; final File file = new File(dir); if (!file.exists()) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.warning("The specified path doesn't refer " + "to an existing folder. Please check the path: " + dir); } } else if (!file.isDirectory()) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.warning("The specified path doesn't refer " + "to a directory. Please check the path: " + dir); } } else { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Setting the Footprints data dir to: " + dir); } footprintsDir = dir; } } FOOTPRINTS_DATA_DIR = footprintsDir; Set<FootprintLoaderSpi> SPI = FootprintLoaderFinder.getAvailableLoaders(); Iterator<FootprintLoaderSpi> iterator = SPI.iterator(); while (iterator.hasNext()) { // Registering all the available loaders LOADERS.add(iterator.next().createLoader()); } } /** A File reference. It can be both a folder, as well as the data file */ private File reference; private volatile FootprintLoader lastLoader; public SidecarFootprintProvider(File reference) { this.reference = reference; } @Override public Geometry getFootprint(SimpleFeature feature) throws IOException { if (feature == null) { // The reference represents the data file itself return getFootprint(reference.getAbsolutePath()); } else { Object value = feature.getAttribute(FOOTPRINT_LOCATION_ATTRIBUTE); if (value != null && value instanceof String) { String strValue = (String) value; String path = getFullPath(strValue); return getFootprint(path); } else { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Could not use the location attribute value to search for " + "a sidecar file, the value was: " + value); } return null; } } } /** * Return the footprint (if any) for a file referred by its path * * @param path * @return * @throws IOException */ public Geometry getFootprint(String path) throws IOException { String noExtension = getNoExtensionPath(path); // Try to reuse over and over the last loader, to avoid checking the others // The last loader is kept in a volatile variable and all the work is done via // a local copy of it to avoid concurrency issues FootprintLoader loader = lastLoader; Geometry result = null; try { // Check from cached loader if (loader != null) { result = loader.loadFootprint(noExtension); } // Check beside the original data if (result == null) { result = checkForFootprint(noExtension); } // Footprint still not found. Fallback by looking in alternative directory if defined if (result == null) { // Try looking at the alternative location if defined if ((noExtension = getAlternativePath(path, true)) != null) { result = checkForFootprint(noExtension); } } } catch (Exception e) { throw new IOException("Failed to load the footprint for granule " + path, e); } return result; } private static String getAlternativePath(String path, boolean removeExtension) { return FOOTPRINTS_DATA_DIR != null ? getAlternativeFile(path, removeExtension).getAbsolutePath() : null; } public static File getAlternativeFile(File file) { return FOOTPRINTS_DATA_DIR != null ? getAlternativeFile(file.getAbsolutePath(), false) : null; } private static File getAlternativeFile(String path, boolean removeExtension) { String basePath = FilenameUtils.getPathNoEndSeparator(path); String name = removeExtension ? FilenameUtils.getBaseName(path) : FilenameUtils.getName(path); String alternativePath = basePath + File.separatorChar + name; return new File(FOOTPRINTS_DATA_DIR, alternativePath); } private Geometry checkForFootprint(String noExtension) { Geometry result = null; for (FootprintLoader test : LOADERS) { try { result = test.loadFootprint(noExtension); if (result != null) { lastLoader = test; break; } } catch (Exception e) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, test.getClass().getName()+" threw exception loading footprint", e); } } } return result; } private String getNoExtensionPath(String path) { int idx = path.lastIndexOf("."); return idx > 0 ? path.substring(0, idx) : path; } private String getFullPath(String strValue) throws IOException { File file = new File(strValue); if (!file.isAbsolute()) { file = new File(reference, strValue); } return file.getCanonicalPath(); } @Override public void dispose() { // nothing to do, in this providers we don't keep files open } public static String getFootprintsDataDir() { return FOOTPRINTS_DATA_DIR; } }