/**
* This file is hereby placed into the Public Domain. This means anyone is
* free to do whatever they wish with this file.
*/
package mil.nga.giat.process.elasticsearch;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.processing.Operations;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.visitor.SimplifyingFilterVisitor;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.process.ProcessException;
import org.geotools.process.factory.DescribeParameter;
import org.geotools.process.factory.DescribeProcess;
import org.geotools.process.factory.DescribeResult;
import org.geotools.process.vector.VectorProcess;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.util.logging.Logging;
import org.opengis.coverage.grid.GridGeometry;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.spatial.BBOX;
import org.opengis.util.ProgressListener;
@DescribeProcess(title = "geoHashGridAgg", description = "Computes a grid from GeoHash grid aggregation buckets with values corresponding to doc_count values.")
public class GeoHashGridProcess implements VectorProcess {
private final static Logger LOGGER = Logging.getLogger(GeoHashGridProcess.class);
private final static FilterFactory FILTER_FACTORY = CommonFactoryFinder.getFilterFactory(null);
public enum Strategy {
BASIC(BasicGeoHashGrid.class),
METRIC(MetricGeoHashGrid.class),
NESTED_AGG(NestedAggGeoHashGrid.class);
private Class<? extends GeoHashGrid> clazz;
private Strategy(Class<? extends GeoHashGrid> clazz) {
this.clazz = clazz;
}
public GeoHashGrid createNewInstance() throws ReflectiveOperationException {
return clazz.getConstructor().newInstance();
}
}
@DescribeResult(name = "result", description = "Output raster")
public GridCoverage2D execute(
// process data
@DescribeParameter(name = "data", description = "Input features") SimpleFeatureCollection obsFeatures,
// process parameters
@DescribeParameter(name = "pixelsPerCell", description = "Resolution used for upsampling (in pixels)", defaultValue="1", min = 1) Integer argPixelsPerCell,
@DescribeParameter(name = "gridStrategy", description = "GeoHash grid strategy", defaultValue="Basic", min = 1) String gridStrategy,
@DescribeParameter(name = "gridStrategyArgs", description = "Grid strategy arguments", min = 0) List<String> gridStrategyArgs,
@DescribeParameter(name = "emptyCellValue", description = "Default cell value", min = 0) Float emptyCellValue,
@DescribeParameter(name = "scaleMin", description = "Scale minimum", defaultValue="0") Float scaleMin,
@DescribeParameter(name = "scaleMax", description = "Scale maximum", min = 0) Float scaleMax,
@DescribeParameter(name = "useLog", description = "Whether to use log values (default=false)", defaultValue="false") Boolean useLog,
// output image parameters
@DescribeParameter(name = "outputBBOX", description = "Bounding box of the output") ReferencedEnvelope argOutputEnv,
@DescribeParameter(name = "outputWidth", description = "Width of output raster in pixels") Integer argOutputWidth,
@DescribeParameter(name = "outputHeight", description = "Height of output raster in pixels") Integer argOutputHeight,
ProgressListener monitor) throws ProcessException {
try {
// construct and populate grid
final GeoHashGrid geoHashGrid = Strategy.valueOf(gridStrategy.toUpperCase()).createNewInstance();
geoHashGrid.setParams(gridStrategyArgs);
geoHashGrid.setEmptyCellValue(emptyCellValue);
geoHashGrid.setScale(new RasterScale(scaleMin, scaleMax, useLog));
geoHashGrid.initalize(argOutputEnv, obsFeatures);
// convert to grid coverage
final GridCoverage2D nativeCoverage = geoHashGrid.toGridCoverage2D();
// reproject
final GridCoverage2D transformedCoverage = (GridCoverage2D) Operations.DEFAULT.resample(nativeCoverage, argOutputEnv.getCoordinateReferenceSystem());
// upscale to approximate output resolution
final GridCoverage2D scaledCoverage = GridCoverageUtil.scale(transformedCoverage, argOutputWidth*argPixelsPerCell, argOutputHeight*argPixelsPerCell);
// crop (geohash grid envelope will always contain output bbox)
final GridCoverage2D croppedCoverage = GridCoverageUtil.crop(scaledCoverage, argOutputEnv);
return GridCoverageUtil.scale(croppedCoverage, argOutputWidth, argOutputHeight);
} catch (Exception e) {
throw new ProcessException("Error executing GeoHashGridProcess", e);
}
}
public Query invertQuery(
@DescribeParameter(name = "outputBBOX", description = "Georeferenced bounding box of the output") ReferencedEnvelope envelope,
Query targetQuery, GridGeometry targetGridGeometry
) throws ProcessException {
final BBOXRemovingFilterVisitor visitor = new BBOXRemovingFilterVisitor();
Filter filter = (Filter) targetQuery.getFilter().accept(visitor, null);
final String geometryName = visitor.getGeometryPropertyName();
if (geometryName != null) {
final BBOX bbox;
try {
if (envelope.getCoordinateReferenceSystem() != null) {
envelope = envelope.transform(DefaultGeographicCRS.WGS84,false);
}
bbox = FILTER_FACTORY.bbox(geometryName, envelope.getMinX(), envelope.getMinY(), envelope.getMaxX(), envelope.getMaxY(), "EPSG:4326");
} catch (Exception e) {
throw new ProcessException("Unable to create bbox filter for feature source", e);
}
filter = (Filter) FILTER_FACTORY.and(filter, bbox).accept(new SimplifyingFilterVisitor(), null);
targetQuery.setFilter(filter);
}
final List<PropertyName> properties = new ArrayList<>();
properties.add(FILTER_FACTORY.property("_aggregation"));
targetQuery.setProperties(properties);
return targetQuery;
}
}