package mil.nga.giat.geowave.format.geotools.raster;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.factory.Hints;
import org.geotools.referencing.CRS;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import mil.nga.giat.geowave.adapter.raster.RasterUtils;
import mil.nga.giat.geowave.adapter.raster.adapter.RasterDataAdapter;
import mil.nga.giat.geowave.core.geotime.store.dimension.GeometryWrapper;
import mil.nga.giat.geowave.core.index.ByteArrayId;
import mil.nga.giat.geowave.core.ingest.GeoWaveData;
import mil.nga.giat.geowave.core.ingest.local.LocalFileIngestPlugin;
import mil.nga.giat.geowave.core.store.CloseableIterator;
import mil.nga.giat.geowave.core.store.CloseableIterator.Wrapper;
import mil.nga.giat.geowave.core.store.adapter.WritableDataAdapter;
import mil.nga.giat.geowave.core.store.index.CommonIndexValue;
import mil.nga.giat.geowave.core.store.index.PrimaryIndex;
/**
* This plugin is used for ingesting any GeoTools supported file data store from
* a local file system directly into GeoWave as GeoTools' SimpleFeatures. It
* supports the default configuration of spatial and spatial-temporal indices
* and does NOT currently support the capability to stage intermediate data to
* HDFS to be ingested using a map-reduce job.
*/
public class GeoToolsRasterDataStoreIngestPlugin implements
LocalFileIngestPlugin<GridCoverage>
{
private final static Logger LOGGER = LoggerFactory.getLogger(GeoToolsRasterDataStoreIngestPlugin.class);
private final RasterOptionProvider optionProvider;
public GeoToolsRasterDataStoreIngestPlugin() {
this(
new RasterOptionProvider());
}
public GeoToolsRasterDataStoreIngestPlugin(
final RasterOptionProvider optionProvider ) {
this.optionProvider = optionProvider;
}
@Override
public String[] getFileExtensionFilters() {
return new String[] {};
}
@Override
public void init(
final File baseDirectory ) {}
@Override
public boolean supportsFile(
final File file ) {
AbstractGridFormat format = null;
try {
format = GridFormatFinder.findFormat(file);
}
catch (final Exception e) {
LOGGER.info(
"Unable to support as raster file",
e);
}
// the null check is enough and we don't need to check the format
// accepts this file because the finder should have previously validated
// this
return (format != null);
}
private static AbstractGridFormat prioritizedFindFormat(
final File input ) {
final AbstractGridFormat format = null;
try {
final Set<AbstractGridFormat> formats = GridFormatFinder.findFormats(input);
if ((formats == null) || formats.isEmpty()) {
LOGGER.warn("Unable to support raster file " + input.getAbsolutePath());
return null;
}
// world image and geotiff can both open tif files, give
// priority to gdalgeotiff, followed by geotiff
for (final AbstractGridFormat f : formats) {
if ("GDALGeoTiff".equals(f.getName())) {
return f;
}
}
for (final AbstractGridFormat f : formats) {
if ("GeoTIFF".equals(f.getName())) {
return f;
}
}
// otherwise just pick the first
final Iterator<AbstractGridFormat> it = formats.iterator();
if (it.hasNext()) {
return it.next();
}
}
catch (final Exception e) {
LOGGER.warn(
"Error while trying read raster file",
e);
return null;
}
return format;
}
@Override
public CloseableIterator<GeoWaveData<GridCoverage>> toGeoWaveData(
final File input,
final Collection<ByteArrayId> primaryIndexIds,
final String globalVisibility ) {
final AbstractGridFormat format = prioritizedFindFormat(input);
if (format == null) {
return new Wrapper(
Collections.emptyIterator());
}
Hints hints = null;
if ((optionProvider.getCrs() != null) && !optionProvider.getCrs().trim().isEmpty()) {
try {
final CoordinateReferenceSystem crs = CRS.decode(optionProvider.getCrs());
if (crs != null) {
hints = new Hints();
hints.put(
Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM,
crs);
}
}
catch (final Exception e) {
LOGGER.warn(
"Unable to find coordinate reference system, continuing without hint",
e);
}
}
final GridCoverage2DReader reader = format.getReader(
input,
hints);
if (reader == null) {
LOGGER.error("Unable to get reader instance, getReader returned null");
return new Wrapper(
Collections.emptyIterator());
}
try {
final GridCoverage2D coverage = reader.read(null);
if (coverage != null) {
final Map<String, String> metadata = new HashMap<String, String>();
final String coverageName = coverage.getName().toString();
try {
// wrapping with try-catch block because often the reader
// does not support operations on coverage name
// if not, we just don't have metadata, and continue
final String[] mdNames = reader.getMetadataNames(coverageName);
if ((mdNames != null) && (mdNames.length > 0)) {
for (final String mdName : mdNames) {
metadata.put(
mdName,
reader.getMetadataValue(
coverageName,
mdName));
}
}
}
catch (final Exception e) {
LOGGER.debug(
"Unable to find metadata from coverage reader",
e);
}
final List<GeoWaveData<GridCoverage>> coverages = new ArrayList<GeoWaveData<GridCoverage>>();
if (optionProvider.isSeparateBands() && (coverage.getNumSampleDimensions() > 1)) {
final String baseName = optionProvider.getCoverageName() != null ? optionProvider.getCoverageName()
: input.getName();
final double[][] nodata = optionProvider.getNodata(coverage.getNumSampleDimensions());
for (int b = 0; b < coverage.getNumSampleDimensions(); b++) {
final RasterDataAdapter adapter = new RasterDataAdapter(
baseName + "_B" + b,
metadata,
(GridCoverage2D) RasterUtils.getCoverageOperations().selectSampleDimension(
coverage,
new int[] {
b
}),
optionProvider.getTileSize(),
optionProvider.isBuildPyramid(),
optionProvider.isBuildHistogream(),
new double[][] {
nodata[b]
});
coverages.add(new GeoWaveData<GridCoverage>(
adapter,
primaryIndexIds,
coverage));
}
}
else {
final RasterDataAdapter adapter = new RasterDataAdapter(
optionProvider.getCoverageName() != null ? optionProvider.getCoverageName()
: input.getName(),
metadata,
coverage,
optionProvider.getTileSize(),
optionProvider.isBuildPyramid(),
optionProvider.isBuildHistogream(),
optionProvider.getNodata(coverage.getNumSampleDimensions()));
coverages.add(new GeoWaveData<GridCoverage>(
adapter,
primaryIndexIds,
coverage));
}
return new Wrapper(
coverages.iterator()) {
@Override
public void close()
throws IOException {
reader.dispose();
}
};
}
else {
LOGGER.warn("Null grid coverage from file '" + input.getAbsolutePath()
+ "' for discovered geotools format '" + format.getName() + "'");
}
}
catch (final IOException e) {
LOGGER.warn(
"Unable to read grid coverage of file '" + input.getAbsolutePath()
+ "' for discovered geotools format '" + format.getName() + "'",
e);
}
return new Wrapper(
Collections.emptyIterator());
}
@Override
public WritableDataAdapter<GridCoverage>[] getDataAdapters(
final String globalVisibility ) {
return new WritableDataAdapter[] {};
}
@Override
public PrimaryIndex[] getRequiredIndices() {
return new PrimaryIndex[] {};
}
@Override
public Class<? extends CommonIndexValue>[] getSupportedIndexableTypes() {
return new Class[] {
GeometryWrapper.class
};
}
}