org.integratedmodelling.engine.geospace.datasources.RegularRasterGridDataSource Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (C) 2007, 2015:
*
* - Ferdinando Villa
* - integratedmodelling.org
* - any other authors listed in @author annotations
*
* All rights reserved. This file is part of the k.LAB software suite,
* meant to enable modular, collaborative, integrated
* development of interoperable data and model components. For
* details, see http://integratedmodelling.org.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Affero General Public License
* Version 3 or any later version.
*
* This program 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
* Affero General Public License for more details.
*
* You should have received a copy of the Affero General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* The license is also available at: https://www.gnu.org/licenses/agpl.html
*******************************************************************************/
package org.integratedmodelling.engine.geospace.datasources;
import java.util.HashMap;
import java.util.Map;
import org.integratedmodelling.api.metadata.IMetadata;
import org.integratedmodelling.api.modelling.IDataSource;
import org.integratedmodelling.api.modelling.IExtent;
import org.integratedmodelling.api.modelling.IObserver;
import org.integratedmodelling.api.modelling.IScale;
import org.integratedmodelling.api.modelling.IValueResolver;
import org.integratedmodelling.api.modelling.contextualization.IStateContextualizer;
import org.integratedmodelling.api.modelling.scheduling.ITransition;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.base.HashableObject;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.common.data.AbstractTableSet;
import org.integratedmodelling.common.metadata.Metadata;
import org.integratedmodelling.common.model.runtime.AbstractStateContextualizer;
import org.integratedmodelling.engine.geospace.Geospace;
import org.integratedmodelling.engine.geospace.coverage.ICoverage;
import org.integratedmodelling.engine.geospace.coverage.raster.AbstractRasterCoverage;
import org.integratedmodelling.engine.geospace.coverage.raster.RasterCoverage;
import org.integratedmodelling.engine.geospace.extents.Grid;
import org.integratedmodelling.engine.geospace.extents.SpaceExtent;
import org.integratedmodelling.engine.geospace.literals.ShapeValue;
import org.integratedmodelling.engine.modelling.runtime.Scale;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabRuntimeException;
import org.integratedmodelling.exceptions.KlabValidationException;
/**
* FIXME MONITORABLE!
* @author ferdinando.villa
*
*/
public abstract class RegularRasterGridDataSource extends HashableObject
implements IDataSource {
protected ICoverage coverage = null;
protected Grid finalExtent = null;
private IMetadata metadata = new Metadata();
protected IMonitor monitor;
protected String id;
int errors = 0;
Map valueMapping = null;
protected boolean isAvailable;
protected Double noData = null;
/*
* set in specialized constructor when we supply it with a coverage that is
* already matched to the final context of use. This happens when a datasource
* is created to provide an accessor for another that has transformed the
* original coverage.
*/
private boolean preMatched = false;
public RegularRasterGridDataSource() {
}
@Override
public IMetadata getMetadata() {
return metadata;
}
public RegularRasterGridDataSource(ICoverage coverage, Grid Grid) {
this.coverage = coverage;
this.finalExtent = Grid;
this.preMatched = true;
}
public RegularRasterGridDataSource(ICoverage coverage, Grid Grid, double noData) {
this.coverage = coverage;
this.finalExtent = Grid;
this.preMatched = true;
this.noData = noData;
}
public void setValueMapping(Map mapping) {
valueMapping = mapping;
}
public Object getValue(int index) {
if (!((AbstractRasterCoverage) coverage).isLoaded()) {
try {
((RasterCoverage) (this.coverage)).loadData();
} catch (KlabException e1) {
throw new KlabRuntimeException(e1);
}
}
try {
Object ret = coverage.getSubdivisionValue(index, finalExtent);
if (!(ret instanceof Number))
return ret;
ret = ((Number) ret).doubleValue();
/*
* TODO/FIXME - this is already done in the coverage, although differently (just
* comparing the value) - will never get here as it will be already a Double.NaN
* if the same nodata values of the coverage are used.
*/
double[] nd = ((AbstractRasterCoverage) coverage).getNodataValue();
if (nd != null && ret != null && (ret instanceof Double)
&& !Double.isNaN((Double) ret)) {
for (double d : nd) {
if (((Double) ret).equals(d)) {
ret = Double.NaN;
break;
}
}
}
return valueMapping == null ? ret
: valueMapping.get(AbstractTableSet.sanitizeKey(ret));
} catch (KlabException e) {
throw new KlabRuntimeException(e);
}
}
@Override
public IStateContextualizer getContextualizer(IScale context, IObserver observer, IMonitor monitor)
throws KlabException {
if (coverage == null) {
coverage = readData();
}
if (!preMatched)
finalExtent = getFinalExtent(context);
return new RasterGridContextualizer(observer, monitor);
}
/*
* -------------------------------------------------------------------------------------------
* read the coverage
* -------------------------------------------------------------------------------------------
*/
/**
* Do whatever is needed to instantiate the coverage.
* @throws KlabException
*/
protected abstract ICoverage readData() throws KlabException;
/**
* Return the final grid extent implied by the context. It should also validate
* the context, ensuring we don't want multiplicity in domains where we cannot
* provide it.
*
* @param context
* @return
*/
protected Grid getFinalExtent(IScale context) throws KlabException {
IExtent space = context.getSpace();
if (!(space instanceof SpaceExtent)) {
throw new KlabValidationException("cannot compute a raster datasource in a non-spatial context");
}
return ((SpaceExtent) space).getGrid() == null ? ((SpaceExtent) space).makeGrid()
: ((SpaceExtent) space).getGrid();
}
/*
* -------------------------------------------------------------------------------------------------
* simple accessor
* -------------------------------------------------------------------------------------------------
*/
class RasterGridContextualizer extends AbstractStateContextualizer
implements IValueResolver {
IObserver observer;
boolean isFirst = true;
public RasterGridContextualizer(IObserver observer, IMonitor monitor) {
super(monitor);
this.observer = observer;
}
@Override
public String toString() {
return "[raster " + coverage.getLayerName() + "]";
}
public String getName() {
return coverage.getLayerName();
}
@Override
public String getLabel() {
return ((AbstractRasterCoverage) coverage).isRasterized()
? "vector data->rasterize" : "raster data";
}
public String getDatasourceLabel() {
return "[raw data from: " + coverage.getLayerName() + "]";
}
@Override
public Map initialize(int index, Map inputs)
throws KlabException {
Map ret = new HashMap<>();
if (isFirst && !preMatched) {
try {
KLAB.info(coverage.getEnvelope() + " -> " + finalExtent);
coverage = coverage
.requireMatch(finalExtent, observer, monitor, false);
KLAB.info("match achieved for " + coverage.getLayerName());
} catch (KlabException e) {
throw new KlabRuntimeException("error retrieving data for "
+ getStateName() + ": "
+ e.getMessage());
}
isFirst = false;
}
ret.put(getStateName(), RegularRasterGridDataSource.this.getValue(index));
return ret;
}
@Override
public Map compute(int index, ITransition transition, Map inputs)
throws KlabException {
return null;
}
@Override
public boolean isProbabilistic() {
return false;
}
}
@Override
public IScale getCoverage() {
Scale ret = new Scale();
try {
coverage = readData();
} catch (KlabException e) {
KLAB.warn("raster data source couldn't be read: " + e.getMessage());
if (monitor != null && errors == 0) {
monitor.error("raster data source " + id + " is inaccessible");
errors++;
}
throw new KlabRuntimeException(e);
}
if (coverage == null) {
throw new KlabRuntimeException("error reading data source " + id);
} else {
try {
ShapeValue shape = new ShapeValue(coverage.getEnvelope());
shape = shape.transform(Geospace.get().getDefaultCRS());
int x = ((AbstractRasterCoverage) coverage).getXCells();
int y = ((AbstractRasterCoverage) coverage).getYCells();
ret.mergeExtent(new SpaceExtent(shape, x, y), true);
} catch (KlabException e) {
// shouldn't happen - just in case
if (monitor != null && errors == 0) {
monitor.error("raster data source " + id + " is corrupted");
errors++;
}
throw new KlabRuntimeException(e);
}
}
return ret;
}
}