package uk.ac.ox.zoo.seeg.abraid.mp.modeloutputhandler.web;
import org.apache.log4j.Logger;
import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.DiseaseExtentClass;
import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.DiseaseService;
import uk.ac.ox.zoo.seeg.abraid.mp.common.util.raster.RasterTransformation;
import uk.ac.ox.zoo.seeg.abraid.mp.common.util.raster.RasterUtils;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
/**
* Helper class to perform the masking transformation on model output rasters.
* Copyright (c) 2015 University of Oxford
*/
public class ModelOutputRasterMaskingHelper {
private static final Logger LOGGER = Logger.getLogger(ModelOutputRasterMaskingHelper.class);
private static final Object LOG_TRANSFORMING_RASTER_DATA =
"Masking out known absence regions in model output raster.";
private final int extentAbsenceValue;
private final WaterBodiesMaskRasterFileLocator waterBodiesMaskRasterFileLocator;
public ModelOutputRasterMaskingHelper(DiseaseService diseaseService,
WaterBodiesMaskRasterFileLocator waterBodiesMaskRasterFileLocator) {
this.waterBodiesMaskRasterFileLocator = waterBodiesMaskRasterFileLocator;
this.extentAbsenceValue = diseaseService.getDiseaseExtentClass(DiseaseExtentClass.ABSENCE).getWeighting();
}
/**
* Creates a new file at the targetFile location based on the sourceRasterFile but where pixels aligned with
* DiseaseExtentClass.ABSENCE pixels in the extentRasterFile have been replaced with extentMaskValue. Pixels aligned
* with known water bodies will also get set to RasterUtils.NO_DATA_VALUE.
* @param targetFile The location at which to write the updated file.
* @param sourceRasterFile The raster to be transformed
* @param extentRasterFile The extent raster to compare against.
* @param extentMaskValue The value to write at DiseaseExtentClass.ABSENCE locations.
* @throws IOException thrown if unable to complete the masking operation.
*/
public void maskRaster(final File targetFile, final File sourceRasterFile,
final File extentRasterFile, final int extentMaskValue) throws IOException {
File[] referenceRasterFiles = new File[] {extentRasterFile, waterBodiesMaskRasterFileLocator.getFile()};
RasterUtils.transformRaster(sourceRasterFile, targetFile, referenceRasterFiles, new RasterTransformation() {
@Override
public void transform(WritableRaster raster, Raster[] referenceRasters) throws IOException {
transformRaster(raster, referenceRasters[0], extentMaskValue, referenceRasters[1]);
}
});
}
private void transformRaster(WritableRaster raster, Raster extentRaster, int extentMaskValue,
Raster waterBodiesMaskRaster) {
LOGGER.info(LOG_TRANSFORMING_RASTER_DATA);
for (int i = 0; i < raster.getWidth(); i++) {
for (int j = 0; j < raster.getHeight(); j++) {
int rasterValue = raster.getSample(i, j, 0);
if (rasterValue != RasterUtils.NO_DATA_VALUE) {
int waterBodiesValue = waterBodiesMaskRaster.getSample(i, j, 0);
if (waterBodiesValue != RasterUtils.NO_DATA_VALUE) {
// Make sure water bodies aren't predicted
raster.setSample(i, j, 0, RasterUtils.NO_DATA_VALUE);
} else {
int extentValue = extentRaster.getSample(i, j, 0);
if (extentValue == extentAbsenceValue) {
// Make sure absence regions aren't predicted
raster.setSample(i, j, 0, extentMaskValue);
} else if (extentValue == RasterUtils.NO_DATA_VALUE) {
// Make sure that areas that we consider to be sea are treated as sea.
// This accounts for the mis-match in extent/admin rasters grids vs the covariates.
raster.setSample(i, j, 0, RasterUtils.NO_DATA_VALUE);
}
}
}
}
}
}
}