/** * 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; } }