/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007 - 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.gce.imagemosaic; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.DecimationPolicy; import org.geotools.coverage.grid.io.GranuleSource; import org.geotools.coverage.grid.io.OverviewPolicy; import org.geotools.coverage.grid.io.footprint.FootprintBehavior; import org.geotools.coverage.grid.io.imageio.ReadType; import org.geotools.data.DataSourceException; import org.geotools.data.DataUtilities; import org.geotools.data.Query; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.factory.Hints; import org.geotools.gce.imagemosaic.SpatialRequestHelper.CoverageProperties; import org.geotools.geometry.jts.ReferencedEnvelope; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.filter.Filter; import org.opengis.metadata.Identifier; import org.opengis.parameter.*; import org.opengis.referencing.ReferenceIdentifier; import javax.media.jai.Interpolation; import java.awt.*; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * A class to handle coverage requests to a reader for a single 2D layer.. * * @author Daniele Romagnoli, GeoSolutions * @author Simone Giannecchini, GeoSolutions */ @SuppressWarnings("rawtypes") public class RasterLayerRequest { /** Logger. */ private final static Logger LOGGER = org.geotools.util.logging.Logging .getLogger(RasterLayerRequest.class); private ReadType readType = AbstractGridFormat.USE_JAI_IMAGEREAD.getDefaultValue() ? ReadType.JAI_IMAGEREAD : ReadType.DIRECT_READ; SpatialRequestHelper spatialRequestHelper; /** The desired decimation Policy for this request */ private DecimationPolicy decimationPolicy; /** The desired overview Policy for this request */ private OverviewPolicy overviewPolicy; /** The Interpolation required to serve this request */ private Interpolation interpolation; private FootprintBehavior footprintBehavior = FootprintBehavior.None; private int defaultArtifactsFilterThreshold = Integer.MIN_VALUE;; private double artifactsFilterPTileThreshold; private boolean heterogeneousGranules = false; RasterManager rasterManager; private Color inputTransparentColor = AbstractGridFormat.INPUT_TRANSPARENT_COLOR .getDefaultValue();; private boolean blend = ImageMosaicFormat.FADING.getDefaultValue(); /** Specifies the behavior for the merging of the final mosaic. */ private MergeBehavior mergeBehavior = MergeBehavior.getDefault(); private Color outputTransparentColor = ImageMosaicFormat.OUTPUT_TRANSPARENT_COLOR .getDefaultValue();; /** * Max number of tiles that this plugin will load. * * If this number is exceeded, i.e. we request an area which is too large instead of getting stuck with opening thousands of files I give you back * a fake coverage. */ private int maximumNumberOfGranules = ImageMosaicFormat.MAX_ALLOWED_TILES.getDefaultValue() .intValue(); private double[] backgroundValues; private Dimension tileDimensions; private boolean multithreadingAllowed; private List<?> requestedTimes; private List<?> elevation; private Filter filter; private boolean accurateResolution; private final Map<String, List> requestedAdditionalDomains = new HashMap<String, List>(); // the bands parameter define the order and which bands should be returned private int[] bands; /** Sort clause on data source. */ private String sortClause; /* * Enables/disables excess granule removal */ private ExcessGranulePolicy excessGranuleRemovalPolicy; private GeneralParameterValue[] params; public List<?> getElevation() { return elevation; } public String getSortClause() { return sortClause; } public GeneralParameterValue[] getParams() { return params; } public void setSortClause(String sortClause) { this.sortClause = sortClause; } public Filter getFilter() { return filter; } public List<?> getRequestedTimes() { return requestedTimes; } public boolean isMultithreadingAllowed() { return multithreadingAllowed; } public DecimationPolicy getDecimationPolicy() { return decimationPolicy; } public boolean isHeterogeneousGranules() { return heterogeneousGranules; } public ExcessGranulePolicy getExcessGranuleRemovalPolicy() { return excessGranuleRemovalPolicy; } public void setExcessGranuleRemovalPolicy(ExcessGranulePolicy policy) { this.excessGranuleRemovalPolicy = policy; } public void setHeterogeneousGranules(final boolean heterogeneousGranules) { this.heterogeneousGranules = heterogeneousGranules; } public RasterManager getRasterManager() { return rasterManager; } public Map<String, List> getRequestedAdditionalDomains() { return new HashMap<String, List>(requestedAdditionalDomains); } /** * Build a new {@code CoverageRequest} given a set of input parameters. * * @param params The {@code GeneralParameterValue}s to initialize this request * @param baseGridCoverage2DReader * @throws IOException */ public RasterLayerRequest(final GeneralParameterValue[] params, final RasterManager rasterManager) throws IOException { this.params = params; // // // // Setting default parameters // // // this.rasterManager = rasterManager; this.heterogeneousGranules = rasterManager.heterogeneousGranules; CoverageProperties coverageProperties = new CoverageProperties(); coverageProperties.setBBox(computeCoverageBoundingBox(rasterManager)); coverageProperties.setRasterArea(rasterManager.spatialDomainManager.coverageRasterArea); coverageProperties .setFullResolution(rasterManager.spatialDomainManager.coverageFullResolution); coverageProperties .setGridToWorld2D(rasterManager.spatialDomainManager.coverageGridToWorld2D); coverageProperties.setCrs2D(rasterManager.spatialDomainManager.coverageCRS2D); coverageProperties .setGeographicBBox(rasterManager.spatialDomainManager.coverageGeographicBBox); coverageProperties .setGeographicCRS2D(rasterManager.spatialDomainManager.coverageGeographicCRS2D); this.spatialRequestHelper = new SpatialRequestHelper(coverageProperties); setDefaultParameterValues(); // // // // Parsing parameter that can be used to control this request // // // if (params != null) { for (GeneralParameterValue gParam : params) { if (gParam instanceof ParameterValue<?>) { final ParameterValue<?> param = (ParameterValue<?>) gParam; final ReferenceIdentifier name = param.getDescriptor().getName(); extractParameter(param, name); } } } // re-compute bbox now that eventual filter has been read if(this.filter != null) { coverageProperties.setBBox(computeCoverageBoundingBox(rasterManager)); } // // // // Set specific imageIO parameters: type of read operation, // imageReadParams // // // checkReadType(); spatialRequestHelper.setAccurateResolution(accurateResolution); spatialRequestHelper.compute(); } private ReferencedEnvelope computeCoverageBoundingBox(final RasterManager rasterManager) throws IOException { if(filter != null && ! Filter.INCLUDE.equals(filter)) { // limit it to the filtered granules bounding box by full enumeration, to avoid // imprecise datastore optimizations (e.g., loose bounds) GranuleSource granules = rasterManager.getGranuleSource(true, null); Query query = new Query(granules.getSchema().getTypeName(), filter); // ... load only the default geometry if possible final GeometryDescriptor gd = granules.getSchema().getGeometryDescriptor(); if(gd != null) { query.setPropertyNames(new String[] {gd.getLocalName()}); } SimpleFeatureCollection features = granules.getGranules(query); ReferencedEnvelope envelope = DataUtilities.bounds(features); if(envelope != null && !envelope.isEmpty()) { return envelope; } } return rasterManager.spatialDomainManager.coverageBBox; } private void setDefaultParameterValues() { // get the read parameters for this format plus the ones for the basic format and set them to the default final ParameterValueGroup readParams = this.rasterManager.parentReader.getFormat() .getReadParameters(); if (readParams == null) { if (LOGGER.isLoggable(Level.FINER)) LOGGER.finer("No default values for the read parameters!"); return; } final List<GeneralParameterDescriptor> parametersDescriptors = readParams.getDescriptor() .descriptors(); for (GeneralParameterDescriptor descriptor : parametersDescriptors) { // we canc get the default vale only with the ParameterDescriptor class if (!(descriptor instanceof ParameterDescriptor)) continue; // get name and default value final ParameterDescriptor desc = (ParameterDescriptor) descriptor; final ReferenceIdentifier name = desc.getName(); final Object value = desc.getDefaultValue(); // // // // Requested GridGeometry2D parameter // // // if (descriptor.getName().equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName())) { if (value == null) continue; final GridGeometry2D gg = (GridGeometry2D) value; spatialRequestHelper.setRequestedGridGeometry(gg); continue; } // // // // Use JAI ImageRead parameter // // // if (name.equals(AbstractGridFormat.USE_JAI_IMAGEREAD.getName())) { if (value == null) continue; readType = ((Boolean) value) ? ReadType.JAI_IMAGEREAD : ReadType.DIRECT_READ; continue; } // // // // Overview Policy parameter // // // if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName())) { if (value == null) continue; overviewPolicy = (OverviewPolicy) value; continue; } // // // // Decimation Policy parameter // // // if (name.equals(AbstractGridFormat.DECIMATION_POLICY.getName())) { if (value == null) continue; decimationPolicy = (DecimationPolicy) value; continue; } // // // // Interpolation parameter // // // if (name.equals(ImageMosaicFormat.INTERPOLATION.getName())) { if (value == null) { continue; } interpolation = (Interpolation) value; continue; } if (name.equals(AbstractGridFormat.INPUT_TRANSPARENT_COLOR.getName())) { if (value == null) continue; inputTransparentColor = (Color) value; // paranoiac check on the provided transparent color inputTransparentColor = new Color(inputTransparentColor.getRed(), inputTransparentColor.getGreen(), inputTransparentColor.getBlue()); continue; } if (name.equals(ImageMosaicFormat.FADING.getName())) { if (value == null) continue; blend = ((Boolean) value).booleanValue(); continue; } if (name.equals(ImageMosaicFormat.OUTPUT_TRANSPARENT_COLOR.getName())) { if (value == null) continue; outputTransparentColor = (Color) value; // paranoiac check on the provided transparent color outputTransparentColor = new Color(outputTransparentColor.getRed(), outputTransparentColor.getGreen(), outputTransparentColor.getBlue()); continue; } if (name.equals(ImageMosaicFormat.BACKGROUND_VALUES.getName())) { if (value == null) continue; backgroundValues = (double[]) value; continue; } if (name.equals(ImageMosaicFormat.MAX_ALLOWED_TILES.getName())) { if (value == null) continue; maximumNumberOfGranules = (Integer) value; continue; } if (name.equals(ImageMosaicFormat.DEFAULT_ARTIFACTS_FILTER_THRESHOLD.getName())) { if (value == null) continue; defaultArtifactsFilterThreshold = (Integer) value; continue; } if (name.equals(ImageMosaicFormat.ARTIFACTS_FILTER_PTILE_THRESHOLD.getName())) { if (value == null) continue; artifactsFilterPTileThreshold = (Double) value; continue; } if (name.equals(ImageMosaicFormat.ALLOW_MULTITHREADING.getName())) { if (value == null) continue; multithreadingAllowed = ((Boolean) value).booleanValue(); continue; } if (name.equals(AbstractGridFormat.FOOTPRINT_BEHAVIOR.getName())) { if (value == null) continue; footprintBehavior = FootprintBehavior.valueOf((String) value); continue; } // // // // Suggested tile size parameter. It must be specified with // the syntax: "TileWidth,TileHeight" (without quotes where TileWidth // and TileHeight are integer values) // // // if (name.equals(AbstractGridFormat.SUGGESTED_TILE_SIZE.getName())) { final String suggestedTileSize = (String) value; // Preliminary checks on parameter value if ((suggestedTileSize != null) && (suggestedTileSize.trim().length() > 0)) { if (suggestedTileSize.contains(AbstractGridFormat.TILE_SIZE_SEPARATOR)) { final String[] tilesSize = suggestedTileSize .split(AbstractGridFormat.TILE_SIZE_SEPARATOR); if (tilesSize.length == 2) { try { // Getting suggested tile size final int tileWidth = Integer.valueOf(tilesSize[0].trim()); final int tileHeight = Integer.valueOf(tilesSize[1].trim()); tileDimensions = new Dimension(tileWidth, tileHeight); } catch (NumberFormatException nfe) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, "Unable to parse " + "suggested tile size parameter"); } } } } } } if (name.equals(ImageMosaicFormat.ACCURATE_RESOLUTION.getName())) { if (value == null) continue; accurateResolution = ((Boolean) value).booleanValue(); return; } if (name.equals(ImageMosaicFormat.EXCESS_GRANULE_REMOVAL.getName())) { if (value == null) continue; excessGranuleRemovalPolicy = (ExcessGranulePolicy) value; return; } } } /** * Set proper fields from the specified input parameter. * * @param param the input {@code ParamaterValue} object * @param name the name of the parameter */ private void extractParameter(ParameterValue<?> param, Identifier name) { // // // // Requested GridGeometry2D parameter // // // if (name.equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName())) { final Object value = param.getValue(); if (value == null) return; final GridGeometry2D gg = (GridGeometry2D) value; spatialRequestHelper.setRequestedGridGeometry(gg.toCanonical()); return; } // // // // Use JAI ImageRead parameter // // // if (name.equals(AbstractGridFormat.USE_JAI_IMAGEREAD.getName())) { final Object value = param.getValue(); if (value == null) return; readType = param.booleanValue() ? ReadType.JAI_IMAGEREAD : ReadType.DIRECT_READ; return; } // // // // Sort clause // // // if (name.equals(ImageMosaicFormat.SORT_BY.getName())) { final Object value = param.getValue(); if (value == null) return; sortClause = param.stringValue(); return; } // // // // Merge Behavior // // // if (name.equals(ImageMosaicFormat.MERGE_BEHAVIOR.getName())) { final Object value = param.getValue(); if (value == null) return; mergeBehavior = MergeBehavior.valueOf(param.stringValue().toUpperCase()); return; } // // // // Overview Policy parameter // // // if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName())) { final Object value = param.getValue(); if (value == null) return; overviewPolicy = (OverviewPolicy) value; if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Requested OverviewPolicy: " + overviewPolicy); } return; } // // // // Decimation Policy parameter // // // if (name.equals(AbstractGridFormat.DECIMATION_POLICY.getName())) { final Object value = param.getValue(); if (value == null) return; decimationPolicy = (DecimationPolicy) value; if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Requested DecimationPolicy: " + decimationPolicy); } return; } // // // // Interpolation parameter // // // if (name.equals(ImageMosaicFormat.INTERPOLATION.getName())) { final Object value = param.getValue(); if (value == null) return; interpolation = (Interpolation) value; if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Requested interpolation: " + interpolation); } return; } if (name.equals(AbstractGridFormat.INPUT_TRANSPARENT_COLOR.getName())) { final Object value = param.getValue(); if (value == null) return; inputTransparentColor = (Color) value; // paranoiac check on the provided transparent color inputTransparentColor = new Color(inputTransparentColor.getRed(), inputTransparentColor.getGreen(), inputTransparentColor.getBlue()); return; } if (name.equals(ImageMosaicFormat.FADING.getName())) { final Object value = param.getValue(); if (value == null) return; blend = ((Boolean) value).booleanValue(); return; } if (name.equals(ImageMosaicFormat.OUTPUT_TRANSPARENT_COLOR.getName())) { final Object value = param.getValue(); if (value == null) return; outputTransparentColor = (Color) value; // paranoiac check on the provided transparent color outputTransparentColor = new Color(outputTransparentColor.getRed(), outputTransparentColor.getGreen(), outputTransparentColor.getBlue()); return; } if (name.equals(ImageMosaicFormat.BACKGROUND_VALUES.getName())) { final Object value = param.getValue(); if (value == null) return; backgroundValues = (double[]) value; return; } if (name.equals(ImageMosaicFormat.MAX_ALLOWED_TILES.getName())) { final Object value = param.getValue(); if (value == null) return; maximumNumberOfGranules = param.intValue(); return; } if (name.equals(ImageMosaicFormat.DEFAULT_ARTIFACTS_FILTER_THRESHOLD.getName())) { final Object value = param.getValue(); if (value == null) return; defaultArtifactsFilterThreshold = param.intValue(); return; } if (name.equals(ImageMosaicFormat.ARTIFACTS_FILTER_PTILE_THRESHOLD.getName())) { final Object value = param.getValue(); if (value == null) return; artifactsFilterPTileThreshold = param.doubleValue(); return; } if (name.equals(ImageMosaicFormat.ALLOW_MULTITHREADING.getName())) { final Object value = param.getValue(); if (value == null) return; multithreadingAllowed = ((Boolean) value).booleanValue(); return; } if (name.equals(AbstractGridFormat.FOOTPRINT_BEHAVIOR.getName())) { final Object value = param.getValue(); if (value == null) return; footprintBehavior = FootprintBehavior.valueOf((String) value); return; } if (name.equals(ImageMosaicFormat.ACCURATE_RESOLUTION.getName())) { final Object value = param.getValue(); if (value == null) { return; } accurateResolution = ((Boolean) value).booleanValue(); return; } // // // // Suggested tile size parameter. It must be specified with // the syntax: "TileWidth,TileHeight" (without quotes where TileWidth // and TileHeight are integer values) // // // if (name.equals(AbstractGridFormat.SUGGESTED_TILE_SIZE.getName())) { final String suggestedTileSize = (String) param.getValue(); // Preliminary checks on parameter value if ((suggestedTileSize != null) && (suggestedTileSize.trim().length() > 0)) { if (suggestedTileSize.contains(AbstractGridFormat.TILE_SIZE_SEPARATOR)) { final String[] tilesSize = suggestedTileSize .split(AbstractGridFormat.TILE_SIZE_SEPARATOR); if (tilesSize.length == 2) { try { // Getting suggested tile size final int tileWidth = Integer.valueOf(tilesSize[0].trim()); final int tileHeight = Integer.valueOf(tilesSize[1].trim()); tileDimensions = new Dimension(tileWidth, tileHeight); } catch (NumberFormatException nfe) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, "Unable to parse " + "suggested tile size parameter"); } } } } } } // // // // Time parameter // // // if (name.equals(ImageMosaicFormat.TIME.getName())) { final Object value = param.getValue(); if (value == null) return; final List<?> dates = (List<?>) value; if (dates == null || dates.size() <= 0) { return; } requestedTimes = dates; return; } // // // Elevation parameter // // // if (name.equals(ImageMosaicFormat.ELEVATION.getName())) { final Object value = param.getValue(); if (value == null) return; elevation = (List<?>) value; return; } // // // // Runtime parameter // // // if (name.equals(ImageMosaicFormat.FILTER.getName())) { final Object value = param.getValue(); if (value == null) return; filter = (Filter) value; return; } // // // // Additional dimension parameter check // // // String paramName = name.getCode(); if (rasterManager.domainsManager != null && rasterManager.domainsManager.isParameterSupported(name)) { final Object value = param.getValue(); if (value == null) { return; } if (value instanceof List) { List values = (List) value; // we are assuming it is a list !!! // remove last comma requestedAdditionalDomains.put(paramName, values); } return; } // setup the the bands parameter which defines the order and the bands that should be returned if (name.equals(ImageMosaicFormat.BANDS.getName())) { // if the parameter is NULL no problem bands = (int[]) param.getValue(); } // see if we have to perform excess granule removal if (name.equals(ImageMosaicFormat.EXCESS_GRANULE_REMOVAL.getName())) { final Object value = param.getValue(); if (value == null) { return; } excessGranuleRemovalPolicy = (ExcessGranulePolicy) value; return; } } /** * @return the accurateResolution */ public boolean isAccurateResolution() { return accurateResolution; } /** * @param accurateResolution the accurateResolution to set */ public void setAccurateResolution(boolean accurateResolution) { this.accurateResolution = accurateResolution; } /** * Check the type of read operation which will be performed and return {@code true} if a JAI imageRead operation need to be performed or * {@code false} if a simple read operation is needed. * * @return {@code true} if the read operation will use a JAI ImageRead operation instead of a simple {@code ImageReader.read(...)} call. */ private void checkReadType() { // // // // First of all check if the ReadType was already set as part the // request parameters // // // if (readType != ReadType.UNSPECIFIED) return; // // // // Ok, the request did not explicitly set the read type, let's check the // hints. // // // final Hints hints = rasterManager.getHints(); if (hints != null) { final Object o = hints.get(Hints.USE_JAI_IMAGEREAD); if (o != null) { readType = (ReadType) o; } } // // // // Last chance is to use the default read type. // // // readType = ReadType.getDefault(); } public Color getInputTransparentColor() { return inputTransparentColor; } public Color getOutputTransparentColor() { return outputTransparentColor; } public int getMaximumNumberOfGranules() { return maximumNumberOfGranules; } public FootprintBehavior getFootprintBehavior() { return footprintBehavior; } public int getDefaultArtifactsFilterThreshold() { return defaultArtifactsFilterThreshold; } public double getArtifactsFilterPTileThreshold() { return artifactsFilterPTileThreshold; } public boolean isBlend() { return blend; } public ReadType getReadType() { return readType; } public double[] getBackgroundValues() { return backgroundValues; } public void setInterpolation(Interpolation interpolation) { this.interpolation = interpolation; } public Interpolation getInterpolation() { return interpolation; } public Dimension getTileDimensions() { return tileDimensions; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("RasterLayerRequest description: \n"); builder.append(spatialRequestHelper).append("\n"); builder.append("\tReadType=").append(readType); return builder.toString(); } public MergeBehavior getMergeBehavior() { return mergeBehavior; } public OverviewPolicy getOverviewPolicy() { return overviewPolicy; } public boolean isEmpty() { return spatialRequestHelper.isEmpty(); } public int[] getBands() { return bands; } }