/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2010, 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.imagemosaic;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.IOUtils;
import org.geotools.data.FeatureSource;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.gce.imagemosaic.Utils.Prop;
import org.geotools.util.Utilities;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import com.vividsolutions.jts.io.WKTWriter;
/**
* Utility class to manage mosaic's footprint
* @author Daniele Romagnoli, GeoSolutions S.A.S.
*
*/
class FootprintUtils {
/**
* A set of properties to be ignored when parsing the properties file. It is
* used to get only the FootprintManagement property, avoiding by this way to load
* and compute useless elements.
*/
final static Set<String> IGNORE_PROPS = new HashSet<String>();
static {
IGNORE_PROPS.add(Prop.ENVELOPE2D);
IGNORE_PROPS.add(Prop.ABSOLUTE_PATH);
IGNORE_PROPS.add(Prop.SUGGESTED_SPI);
IGNORE_PROPS.add(Prop.EXP_RGB);
IGNORE_PROPS.add(Prop.LEVELS);
IGNORE_PROPS.add(Prop.LOCATION_ATTRIBUTE);
IGNORE_PROPS.add(Prop.NAME);
}
final static String FOOTPRINT_EXT = ".fpt";
final static String FOOTPRINT_PREFIX = "footprint";
final static String FOOTPRINT = FOOTPRINT_PREFIX + ".shp";
private FootprintUtils() {
}
private final static Logger LOGGER = org.geotools.util.logging.Logging.getLogger(FootprintUtils.class);
/**
* Given a footprint summary file (.fpt), populate the provided
* footprints <ID-Geometry> pairs Map
*
* @param footprintSummaryFile
* the footprint summary file.
* @param footprintsIDGeometryMap
* the Map to be populated
*/
static void initFootprintsGranuleIDGeometryMap(
final File footprintSummaryFile,
final Map<String, Geometry> footprintsIDGeometryMap) {
Utilities.ensureNonNull("footprintSummaryFile", footprintSummaryFile);
if (!footprintSummaryFile.exists() || !footprintSummaryFile.canRead()) {
throw new IllegalArgumentException("Unable to access to the provided footprint file " + footprintSummaryFile.getAbsolutePath());
}
Utilities.ensureNonNull("footprintsID_GeometryMap", footprintsIDGeometryMap);
FileReader reader = null;
try {
reader=new FileReader(footprintSummaryFile);
BufferedReader bReader = new BufferedReader(reader);
String footprint;
final WKTReader geometryReader = new WKTReader();
while ((footprint = bReader.readLine()) != null) {
String fpt[] = footprint.split("=");
if (fpt.length == 2) {
// parse the geometry
footprintsIDGeometryMap.put(fpt[0], geometryReader.read(fpt[1]));
}
}
bReader.close();
} catch (IOException e) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, e.getLocalizedMessage(), e);
}
} catch (ParseException e) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, e.getLocalizedMessage(), e);
}
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (Throwable e) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, e.getLocalizedMessage(), e);
}
IOUtils.closeQuietly(reader);
}
}
}
/**
* Look for a footprint geometry for the provided featureID.
*
* @param featureID
* the ID of a feature for which the footprint should be searched
* @param footprintsMap
* the map containing <.ID,Geometry> pairs
* @return the related {@link Geometry} if found, or null otherwise.
*/
static Geometry lookupFootprintGeometry(
final String featureID,
final Map<String, Geometry> footprintsMap) {
Utilities.ensureNonNull("featureID", featureID);
Utilities.ensureNonNull("footprintsMap", footprintsMap);
if (footprintsMap != null && !footprintsMap.isEmpty()) {
final String id = featureID.substring(featureID.lastIndexOf(".") + 1, featureID.length());
return footprintsMap.containsKey(id) ? footprintsMap.get(id) : null;
}
return null;
}
/**
* Init the provided footprint map containing <String(location), Geometry(footprint)> pairs.
*
* @throws IOException
*/
static void initFootprintsLocationGeometryMap(
final ShapefileDataStore footprintStore,
final Map<String, Geometry> footprintsMap) throws IOException {
Utilities.ensureNonNull("footprintStore", footprintStore);
Utilities.ensureNonNull("footprintsMap", footprintsMap);
final String[] typeNames = footprintStore.getTypeNames();
if (typeNames.length <= 0)
throw new IllegalArgumentException("Problems when opening the footprint, no typenames for the schema are defined");
final String typeName = typeNames[0];
final FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = footprintStore.getFeatureSource(typeName);
final FeatureCollection<SimpleFeatureType, SimpleFeature> features = featureSource.getFeatures();
if (features == null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("No features found in the footprint");
}
return;
}
FeatureIterator<SimpleFeature> it = null;
try {
// load the feature from the footprint shapefile store
it = features.features();
if (!it.hasNext()) {
throw new IllegalArgumentException("The provided FeatureCollection<SimpleFeatureType, SimpleFeature> or empty, it's impossible to create an index!");
}
// now add the footprint to the Map
while (it.hasNext()) {
final SimpleFeature feature = it.next();
final Geometry g = (Geometry) feature.getDefaultGeometry();
final String location = (String) feature.getAttribute("location");
footprintsMap.put(location, g);
}
} finally {
try {
if (it != null) {
it.close();
}
} catch (Throwable e) {
}
try {
it.close();
} catch (Throwable e) {
}
}
}
/**
* Build a "ID=Geometry" pair for the provided feature ID, by
* looking for a geometry in the provided footprintsMap for the specified
* locationKey
*
* @param footprintGeometryMap
* @param featureID
* @param locationKey
* @param writer
* @return
*/
private static String buildIDGeometryPair(
final Map<String, Geometry> footprintGeometryMap,
final String featureID,
final String locationKey,
final WKTWriter writer) {
String idGeometryPair = "";
Utilities.ensureNonNull("featureID", featureID);
Utilities.ensureNonNull("writer", writer);
Utilities.ensureNonNull("locationKey", locationKey);
Utilities.ensureNonNull("footprintGeometryMap", footprintGeometryMap);
if (!footprintGeometryMap.isEmpty() && footprintGeometryMap.containsKey(locationKey)) {
final Geometry polygon = (Geometry) footprintGeometryMap.get(locationKey);
if (polygon != null){
final String s = writer.write(polygon);
String id = featureID;
id = id.substring(id.lastIndexOf(".") + 1, id.length());
idGeometryPair = new StringBuilder(id).append("=").append(s).append("\n").toString();
}
}
return idGeometryPair;
}
/**
* Write a footprint summary file (".fpt") given an input <String,Geometry>
* map containing granule location and related geometry, and the index
* shapefile store to associate footprints to granules.
*
* @param footprintSummaryFile
* the output footprint summary file
* @param footprintsLocationGeometryMap
* the map containing <granuleLocation,footprintGeometry> pairs
* @throws MalformedURLException In case the url we create internally for the mosaic index is wrong (should never happen)
*/
static void writeFootprintSummary(
final File footprintSummaryFile,
final File indexFile,
final Map<String, Geometry> footprintsLocationGeometryMap) throws MalformedURLException {
Utilities.ensureNonNull("footprintSummaryFile", footprintSummaryFile);
Utilities.ensureNonNull("indexFile", indexFile);
Utilities.ensureNonNull("footprintsLocationGeometryMap", footprintsLocationGeometryMap);
if (footprintsLocationGeometryMap.isEmpty())
return;
final ShapefileDataStore store = new ShapefileDataStore(indexFile.toURI().toURL());
if (footprintsLocationGeometryMap.isEmpty())
return;
final String[] typeNames = store.getTypeNames();
if (typeNames.length <= 0) {
throw new IllegalArgumentException("Problems when opening the shapefile, no typenames for the schema are defined");
}
final String typeName = typeNames[0];
FileWriter footprintWriter = null;
FeatureIterator<SimpleFeature> it=null;
try {
final FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = store.getFeatureSource(typeName);
final FeatureCollection<SimpleFeatureType, SimpleFeature> features = featureSource.getFeatures();
if (features == null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("No features found in the shapefile");
}
return;
}
// load the feature from the shapefile
it = features.features();
if (!it.hasNext())
throw new IllegalArgumentException("The provided FeatureCollection<SimpleFeatureType, SimpleFeature> or empty, it's impossible to create an index!");
footprintWriter = new FileWriter(footprintSummaryFile);
final BufferedWriter writer = new BufferedWriter(footprintWriter);
final WKTWriter geometryWriter = new WKTWriter();
// Scan the index shapefile to get granules location
while (it.hasNext()) {
final SimpleFeature feature = it.next();
final String location = (String) feature.getAttribute("location");
if (location != null && location.trim().length() > 0) {
final String locationKey = location;
// Check if a footprint exist in the map for this granule
if (footprintsLocationGeometryMap.containsKey(locationKey)) {
// Build a featureID=Geometry pair and write it in
// the Footprint summary file
final String idGeometryPair = FootprintUtils.buildIDGeometryPair(footprintsLocationGeometryMap, feature.getID(), locationKey,geometryWriter);
writer.write(idGeometryPair);
}
}
}
writer.flush();
writer.close();
} catch (Throwable e) {
// ignore exception
if (LOGGER.isLoggable(Level.FINEST))
LOGGER.log(Level.FINEST, e.getLocalizedMessage(), e);
} finally {
try {
if (it != null) {
it.close();
}
} catch (Throwable e) {
if (LOGGER.isLoggable(Level.FINEST))
LOGGER.log(Level.FINEST, e.getLocalizedMessage(), e);
}
try {
if (footprintWriter != null) {
footprintWriter.flush();
footprintWriter.close();
}
} catch (Throwable e) {
if (LOGGER.isLoggable(Level.FINEST))
LOGGER.log(Level.FINEST, e.getLocalizedMessage(), e);
IOUtils.closeQuietly(footprintWriter);
}
footprintWriter = null;
try{
store.dispose();
}catch (Throwable e) {
if (LOGGER.isLoggable(Level.FINEST))
LOGGER.log(Level.FINEST, e.getLocalizedMessage(), e);
}
}
}
/**
* Search the footprint shape file in the specified directory.
*
* @param indexingDirectory
* @return
*/
static File searchFootprint(final String indexingDirectory) {
File footprintFile = null;
if (indexingDirectory != null && indexingDirectory.trim().length() > 0) {
final File file = new File(indexingDirectory,FootprintUtils.FOOTPRINT);
if (file != null && file.exists()) {
footprintFile = file;
}
}
return footprintFile;
}
}