/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2014 Geomatys.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.constellation.provider;
import org.apache.sis.measure.MeasurementRange;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.storage.DataStoreException;
import org.geotoolkit.coverage.grid.GeneralGridGeometry;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.io.GridCoverageReadParam;
import org.geotoolkit.coverage.io.GridCoverageReader;
import org.geotoolkit.data.query.QueryBuilder;
import org.geotoolkit.display.PortrayalException;
import org.geotoolkit.image.io.metadata.SpatialMetadata;
import org.geotoolkit.map.DefaultCoverageMapLayer;
import org.geotoolkit.map.MapBuilder;
import org.geotoolkit.map.MapLayer;
import org.geotoolkit.referencing.CRS;
import org.geotoolkit.style.DefaultStyleFactory;
import org.geotoolkit.style.MutableStyle;
import org.geotoolkit.style.StyleConstants;
import org.opengis.filter.Filter;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.TemporalCRS;
import org.opengis.referencing.crs.VerticalCRS;
import java.awt.*;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import org.apache.sis.measure.NumberRange;
import org.geotoolkit.coverage.combineIterator.GridCombineIterator;
import org.geotoolkit.internal.referencing.CRSUtilities;
import org.geotoolkit.coverage.GridSampleDimension;
import org.geotoolkit.coverage.filestore.FileCoverageReference;
import org.geotoolkit.storage.coverage.CoverageReference;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.FactoryException;
import org.opengis.util.GenericName;
/**
* Regroups information about a {@linkplain org.constellation.provider.LayerDetails layer}.
*
* @author Johann Sorel (Geomatys)
*/
public class DefaultCoverageData extends AbstractData implements CoverageData {
private static final MutableStyle DEFAULT =
new DefaultStyleFactory().style(StyleConstants.DEFAULT_RASTER_SYMBOLIZER);
private final CoverageReference ref;
public DefaultCoverageData(final GenericName name, final CoverageReference ref){
super(name, Collections.EMPTY_LIST);
this.ref = ref;
}
@Override
public Object getOrigin() {
return ref;
}
/**
* {@inheritDoc}
*/
@Override
public GridCoverage2D getCoverage(final Envelope envelope, final Dimension dimension, final Double elevation,
final Date time) throws DataStoreException, IOException
{
final GridCoverageReader reader = ref.acquireReader();
GridCoverageReadParam param = new GridCoverageReadParam();
if (envelope != null) {
param.setEnvelope(envelope);
}
try {
return (GridCoverage2D) reader.read(ref.getImageIndex(), param);
} catch (CancellationException ex) {
throw new IOException(ex.getMessage(),ex);
}finally{
ref.recycle(reader);
}
}
/**
* {@inheritDoc}
*/
@Override
public MapLayer getMapLayer(MutableStyle style, final Map<String, Object> params) throws PortrayalException {
if(style == null){
style = getDefaultStyle();
}
final MapLayer layer;
layer = MapBuilder.createCoverageLayer(ref, style);
// EXTRA FILTER extra parameter ////////////////////////////////////////
if (params != null && layer instanceof DefaultCoverageMapLayer) {
final Map<String,?> extras = (Map<String, ?>) params.get(KEY_EXTRA_PARAMETERS);
if (extras != null) {
Filter filter = null;
for (String key : extras.keySet()) {
if (key.equalsIgnoreCase("cql_filter")) {
final Object extra = extras.get(key);
String cqlFilter = null;
if (extra instanceof List) {
cqlFilter = ((List) extra).get(0).toString();
} else if (extra instanceof String){
cqlFilter = (String)extra;
}
if (cqlFilter != null){
filter = buildCQLFilter(cqlFilter, filter);
}
} else if (key.startsWith("dim_") || key.startsWith("DIM_")){
final String dimValue = ((List)extras.get(key)).get(0).toString();
final String dimName = key.substring(4);
filter = buildDimFilter(dimName, dimValue, filter);
}
}
if (filter != null) {
final DefaultCoverageMapLayer cml = (DefaultCoverageMapLayer) layer;
cml.setQuery(QueryBuilder.filtered(cml.getCoverageReference().getName(), filter));
}
}
}
////////////////////////////////////////////////////////////////////////
return layer;
}
/**
* {@inheritDoc}
*/
private MutableStyle getDefaultStyle() {
return DEFAULT;
}
/**
* Returns an empty set.
*/
@Override
public SortedSet<Date> getAvailableTimes() throws DataStoreException {
SortedSet<Date> dates = new TreeSet<>();
final Envelope env = getEnvelope();
try {
final CoordinateReferenceSystem crs = env.getCoordinateReferenceSystem();
final int tempIndex = CRSUtilities.getDimensionOf(crs, TemporalCRS.class);
if (tempIndex != -1) {
final TemporalCRS temporalCRS = org.apache.sis.referencing.CRS.getTemporalComponent(crs);
final MathTransform mt = CRS.findMathTransform(temporalCRS, CommonCRS.Temporal.JAVA.crs());
final GridCoverageReader reader = ref.acquireReader();
//we extract value at pixel center for dates
final GeneralGridGeometry gridGeometry = reader.getGridGeometry(ref.getImageIndex());
final NumberRange[] tempValues = GridCombineIterator.extractAxisRanges(gridGeometry.getExtent(),gridGeometry.getGridToCRS(PixelInCell.CELL_CENTER), tempIndex);
ref.recycle(reader);
double[] sourceDate = new double[1];
for (NumberRange nR : tempValues) {
//transform extracted values into java time units (timestamp)
sourceDate[0] = (nR.getMinDouble() + nR.getMaxDouble()) / 2.0;
mt.transform(sourceDate,0,sourceDate,0, 1);
dates.add(new Date(Double.valueOf(sourceDate[0]).longValue()));
}
}
} catch (FactoryException | TransformException e) {
throw new DataStoreException("Unable to extract available times from layer "+name, e);
}
return dates;
}
/**
* Returns an empty set.
*/
@Override
public SortedSet<Number> getAvailableElevations() throws DataStoreException {
SortedSet<Number> elevations = new TreeSet<>();
final Envelope env = getEnvelope();
final CoordinateReferenceSystem crs = env.getCoordinateReferenceSystem();
try {
final int tempIndex = CRSUtilities.getDimensionOf(crs, VerticalCRS.class);
if (tempIndex != -1) {
final VerticalCRS verticalCRS = org.apache.sis.referencing.CRS.getVerticalComponent(crs, true);
final MathTransform mt = CRS.findMathTransform(verticalCRS, CommonCRS.Vertical.ELLIPSOIDAL.crs());
final GridCoverageReader reader = ref.acquireReader();
final NumberRange[] tempValues = GridCombineIterator.extractAxisRanges(reader.getGridGeometry(ref.getImageIndex()), tempIndex);
ref.recycle(reader);
double[] sourceEle = new double[1];
for (NumberRange nR : tempValues) {
//transform extracted values into ellipsoid elevation
sourceEle[0] = nR.getMinDouble();
mt.transform(sourceEle,0,sourceEle,0, 1);
elevations.add(sourceEle[0]);
}
}
} catch (FactoryException | TransformException e) {
throw new DataStoreException("Unable to extract available times from layer "+name, e);
}
return elevations;
}
@Override
public MeasurementRange<?>[] getSampleValueRanges() {
return new MeasurementRange<?>[0];
}
@Override
public TYPE getType() {
return TYPE.COVERAGE;
}
@Override
public Envelope getEnvelope() throws DataStoreException {
final GridCoverageReader reader = ref.acquireReader();
try {
final GeneralGridGeometry generalGridGeom = reader.getGridGeometry(ref.getImageIndex());
if (generalGridGeom == null || generalGridGeom.getEnvelope() == null) {
LOGGER.log(Level.INFO, "The layer \"{0}\" does not contain a grid geometry information.", name);
return null;
}
return generalGridGeom.getEnvelope();
} catch (CancellationException ex) {
throw new DataStoreException(ex);
} finally {
ref.recycle(reader);
}
}
@Override
public String getImageFormat() {
if (ref instanceof FileCoverageReference) {
FileCoverageReference fref = (FileCoverageReference) ref;
if (fref.getSpi() != null &&
fref.getSpi().getMIMETypes() != null &&
fref.getSpi().getMIMETypes().length > 0) {
return fref.getSpi().getMIMETypes()[0];
}
}
return "";
}
@Override
public String getRemarks() {
return "";
}
@Override
public String getThematic() {
return "";
}
@Override
public SpatialMetadata getSpatialMetadata() throws DataStoreException {
final GridCoverageReader reader = ref.acquireReader();
try {
return reader.getCoverageMetadata(0);
} catch (CancellationException ex) {
throw new DataStoreException(ex);
} finally {
ref.recycle(reader);
}
}
@Override
public List<GridSampleDimension> getSampleDimensions() throws DataStoreException {
final GridCoverageReader reader = ref.acquireReader();
try {
return reader.getSampleDimensions(ref.getImageIndex());
} catch (CancellationException ex) {
throw new DataStoreException(ex);
} finally {
ref.recycle(reader);
}
}
}