package org.constellation.coverage.process;
import static org.constellation.coverage.process.AbstractPyramidCoverageDescriptor.IN_COVERAGE_REF;
import static org.constellation.coverage.process.AbstractPyramidCoverageDescriptor.ORIGINAL_DATA;
import static org.constellation.coverage.process.AbstractPyramidCoverageDescriptor.PROVIDER_OUT_ID;
import static org.constellation.coverage.process.AbstractPyramidCoverageDescriptor.PYRAMID_CRS;
import static org.constellation.coverage.process.AbstractPyramidCoverageDescriptor.PYRAMID_DATASET;
import static org.constellation.coverage.process.AbstractPyramidCoverageDescriptor.PYRAMID_FOLDER;
import static org.constellation.coverage.process.AbstractPyramidCoverageDescriptor.PYRAMID_NAME;
import static org.constellation.coverage.process.AbstractPyramidCoverageDescriptor.UPDATE;
import static org.geotoolkit.parameter.Parameters.getOrCreate;
import static org.geotoolkit.parameter.Parameters.value;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.storage.DataStoreException;
import org.constellation.business.IDataBusiness;
import org.constellation.business.IProviderBusiness;
import org.constellation.database.api.jooq.tables.pojos.Data;
import org.constellation.database.api.jooq.tables.pojos.Dataset;
import org.constellation.process.AbstractCstlProcess;
import org.constellation.provider.DataProvider;
import org.constellation.provider.DataProviderFactory;
import org.constellation.provider.DataProviders;
import org.constellation.provider.ProviderFactoryType;
import org.constellation.provider.configuration.ProviderParameters;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.grid.GridGeometry2D;
import org.geotoolkit.coverage.grid.ViewType;
import org.geotoolkit.coverage.xmlstore.XMLCoverageStore;
import org.geotoolkit.coverage.xmlstore.XMLCoverageStoreFactory;
import org.geotoolkit.internal.coverage.CoverageUtilities;
import org.geotoolkit.internal.referencing.CRSUtilities;
import org.geotoolkit.process.ProcessDescriptor;
import org.geotoolkit.process.ProcessException;
import org.geotoolkit.referencing.CRS;
import org.geotoolkit.referencing.OutOfDomainOfValidityException;
import org.geotoolkit.storage.coverage.AbstractCoverageStoreFactory;
import org.geotoolkit.storage.coverage.CoverageReference;
import org.geotoolkit.storage.coverage.CoverageStore;
import org.geotoolkit.storage.coverage.CoverageStoreFinder;
import org.geotoolkit.storage.coverage.Pyramid;
import org.geotoolkit.storage.coverage.PyramidSet;
import org.geotoolkit.storage.coverage.PyramidalCoverageReference;
import org.geotoolkit.utility.parameter.ParametersExt;
import org.opengis.geometry.Envelope;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.crs.CompoundCRS;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.SingleCRS;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.FactoryException;
import org.opengis.util.GenericName;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author Quentin Boileau (Geomatys)
*/
public abstract class AbstractPyramidCoverageProcess extends AbstractCstlProcess {
@Autowired
protected IDataBusiness dataBusiness;
@Autowired
protected IProviderBusiness providerBusiness;
protected static final String PNG_FORMAT = "PNG";
protected static final String TIFF_FORMAT = "TIFF";
protected static final int TILE_SIZE = 256;
public AbstractPyramidCoverageProcess(ProcessDescriptor desc, ParameterValueGroup parameter) {
super(desc, parameter);
}
protected static void fillParameters(CoverageReference inCoverageRef, Data orinigalData, String pyramidName,
String providerID, File pyramidFolder, Dataset dataset,
CoordinateReferenceSystem[] pyramidCRS, Boolean update, ParameterValueGroup params) {
getOrCreate(IN_COVERAGE_REF, params).setValue(inCoverageRef);
getOrCreate(ORIGINAL_DATA, params).setValue(orinigalData);
getOrCreate(PYRAMID_NAME, params).setValue(pyramidName);
getOrCreate(PROVIDER_OUT_ID, params).setValue(providerID);
getOrCreate(PYRAMID_FOLDER, params).setValue(pyramidFolder);
getOrCreate(PYRAMID_DATASET, params).setValue(dataset);
getOrCreate(PYRAMID_CRS, params).setValue(pyramidCRS);
getOrCreate(UPDATE, params).setValue(update);
}
protected CoverageStore getOrCreateXMLCoverageStore(final File pyramidFolder)
throws DataStoreException, ProcessException, MalformedURLException {
final ParameterValueGroup storeParams = XMLCoverageStoreFactory.PARAMETERS_DESCRIPTOR.createValue();
getOrCreate(XMLCoverageStoreFactory.NAMESPACE, storeParams).setValue("no namespace");
getOrCreate(XMLCoverageStoreFactory.PATH, storeParams).setValue(pyramidFolder.toURI().toURL());
getOrCreate(XMLCoverageStoreFactory.CACHE_TILE_STATE, storeParams).setValue(true);
final XMLCoverageStore store = (XMLCoverageStore) CoverageStoreFinder.open(storeParams);
if (store == null) {
throw new ProcessException("Can't initialize XMLCoverageStore.", this, null);
}
return store;
}
protected CoverageReference getOrCreateCRef(XMLCoverageStore coverageStore, GenericName coverageName, String tileFormat, ViewType type)
throws DataStoreException {
CoverageReference cv = null;
for (GenericName n : coverageStore.getNames()) {
if (n.tip().toString().equals(coverageName.tip().toString())) {
cv = coverageStore.getCoverageReference(n);
}
}
if (cv == null) {
cv = coverageStore.create(coverageName, type, tileFormat);
}
return cv;
}
/**
* Compute bounds of pyramid CRS
* @return
*/
protected Envelope getPyramidWorldEnvelope(CoordinateReferenceSystem crs){
return CRS.getEnvelope(crs);
}
/**
* Change 2D part of input envelope with base 2D CRS validity domain.
* @param pyramidCRS2D 2D CRS of the final envelope.
* @param coverageEnvelope potentially nD Envelope in which 2D ranges will be changed.
* @return Envelope with same number of dimension than input {@code coverageEnvelope} and geographical
* CRS equals to {@code pyramidCRS2D}.
*/
protected Envelope getFixedPyramidEnvelop(CoordinateReferenceSystem pyramidCRS2D,
Envelope coverageEnvelope) {
GeneralEnvelope finalPyramidEnv = GeneralEnvelope.castOrCopy(CRS.getEnvelope(pyramidCRS2D));
CoordinateReferenceSystem coverageCRS = coverageEnvelope.getCoordinateReferenceSystem();
if (coverageCRS instanceof CompoundCRS) {
finalPyramidEnv = GeneralEnvelope.castOrCopy(CRSUtilities.appendMissingDimensions(finalPyramidEnv, (CompoundCRS) coverageCRS));
assert finalPyramidEnv != null;
final int minOrdi0 = org.geotoolkit.storage.coverage.CoverageUtilities.getMinOrdinate(coverageCRS);
final int minOrdi1 = minOrdi0 + 1;
final int nbDim = coverageCRS.getCoordinateSystem().getDimension();
for (int d = 0; d < nbDim; d++) {
if (d != minOrdi0 && d != minOrdi1) {
//set extra dimension range
finalPyramidEnv.setRange(d, coverageEnvelope.getMinimum(d), coverageEnvelope.getMaximum(d));
}
}
}
return finalPyramidEnv;
}
/**
* Re-use previous pyramid scales or compute new scales from input coverage.
* @param inputCoverage
* @param pyramidRef
* @return scales array
* @throws DataStoreException
* @throws FactoryException
*/
protected double[] getPyramidScales(GridCoverage2D inputCoverage, PyramidalCoverageReference pyramidRef,
CoordinateReferenceSystem pyramidCRS)
throws DataStoreException, FactoryException, TransformException, OutOfDomainOfValidityException {
final PyramidSet pyramidSet = pyramidRef.getPyramidSet();
final Collection<Pyramid> pyramids = pyramidSet.getPyramids();
Pyramid pyramid = null;
for (Pyramid p : pyramids) {
if (CRS.equalsIgnoreMetadata(p.getCoordinateReferenceSystem(), pyramidCRS)) {
pyramid = p;
break;
}
}
double[] scales;
if (pyramid != null) {
scales = pyramid.getScales();
} else {
scales = computeScales(inputCoverage, pyramidCRS);
}
return scales;
}
private double[] computeScales(GridCoverage2D coverage, final CoordinateReferenceSystem pyramidCRS)
throws TransformException, OutOfDomainOfValidityException {
final SingleCRS pyramidCRS2D = org.apache.sis.referencing.CRS.getHorizontalComponent(pyramidCRS);
final Envelope env = getPyramidWorldEnvelope(pyramidCRS2D);
final int minBOrdi = CRSUtilities.firstHorizontalAxis(pyramidCRS2D);
final double spanX = env.getSpan(minBOrdi);
final GridGeometry2D gg = coverage.getGridGeometry();
final Envelope covEnv = CRS.transform(gg.getEnvelope(), env.getCoordinateReferenceSystem());
final double baseScale = covEnv.getSpan(minBOrdi) / gg.getExtent2D().getSpan(minBOrdi);
double scale = spanX / TILE_SIZE;
return (double[]) CoverageUtilities.toWellKnownScale(env, new NumberRange(Double.class, baseScale, true, scale, true)).getValue();
}
/**
* Create provider and data and link them to already existing dataset.
*
* @param providerID
* @param store
* @param domainId
* @param datasetId
* @return new provider
* @throws ProcessException
*/
protected DataProvider createProvider(final String providerID, CoverageStore store,
final Integer datasetId) throws ProcessException {
final DataProvider outProvider;
try {
//get store configuration
final ParameterValueGroup storeConf = store.getConfiguration();
final String namespace = value(AbstractCoverageStoreFactory.NAMESPACE, storeConf);
final URL pyramidFolder = value(XMLCoverageStoreFactory.PATH, storeConf);
//create provider configuration
final String factoryName = ProviderFactoryType.COVERAGE_STORE.getType();
final DataProviderFactory factory = DataProviders.getInstance().getFactory(factoryName);
final ParameterValueGroup pparams = factory.getProviderDescriptor().createValue();
ParametersExt.getOrCreateValue(pparams, ProviderParameters.SOURCE_ID_DESCRIPTOR.getName().getCode()).setValue(providerID);
ParametersExt.getOrCreateValue(pparams, ProviderParameters.SOURCE_TYPE_DESCRIPTOR.getName().getCode()).setValue(factoryName);
final ParameterValueGroup choiceparams = ParametersExt.getOrCreateGroup(pparams, factory.getStoreDescriptor().getName().getCode());
final ParameterValueGroup xmlpyramidparams = ParametersExt.getOrCreateGroup(choiceparams, XMLCoverageStoreFactory.PARAMETERS_DESCRIPTOR.getName().getCode());
ParametersExt.getOrCreateValue(xmlpyramidparams, XMLCoverageStoreFactory.PATH.getName().getCode()).setValue(pyramidFolder);
ParametersExt.getOrCreateValue(xmlpyramidparams, XMLCoverageStoreFactory.NAMESPACE.getName().getCode()).setValue(namespace);
outProvider = DataProviders.getInstance().createProvider(providerID, factory, pparams, datasetId);
} catch (Exception ex) {
throw new ProcessException("Failed to create pyramid provider "+ex.getMessage(), this, ex);
}
return outProvider;
}
}