ingo@1127: /*
ingo@1127: * Copyright (c) 2010 by Intevation GmbH
ingo@1127: *
ingo@1127: * This program is free software under the LGPL (>=v2.1)
ingo@1127: * Read the file LGPL.txt coming with the software for details
ingo@1127: * or visit http://www.gnu.org/licenses/ if it does not exist.
ingo@1127: */
ingo@1127:
tim@544: package de.intevation.gnv.geobackend.sde.datasources;
tim@544:
tim@544: import com.vividsolutions.jts.geom.Coordinate;
tim@544:
sascha@545: import java.io.Serializable;
sascha@545:
sascha@550: import org.apache.commons.math.ArgumentOutsideDomainException;
sascha@550:
sascha@552: import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
sascha@552:
sascha@550: import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
sascha@550:
sascha@547: import org.apache.log4j.Logger;
sascha@547:
tim@544: /**
sascha@887: * @author Tim Englich
sascha@887: * @author Sascha L. Teichmann
tim@544: */
sascha@885: public class RasterObject
sascha@545: implements Serializable
sascha@545: {
sascha@547: private static Logger log = Logger.getLogger(RasterObject.class);
sascha@547:
sascha@547: public static final int NEAREST_NEIGHBOR = 0;
sascha@547: public static final int BILINEAR = 1;
sascha@550: public static final int BICUBIC = 2;
sascha@547:
sascha@547: private double mx;
sascha@547: private double bx;
sascha@547: private double my;
sascha@547: private double by;
sascha@547:
tim@544: private int columnIndex;
sascha@547: private int rowIndex;
sascha@547:
sascha@547: private int tileWidth;
sascha@547: private int tileHeight;
sascha@547:
sascha@547: private double [] rasterData;
sascha@547:
sascha@550: private transient PolynomialSplineFunction [] splines;
sascha@550: private transient double [] columns;
sascha@550:
sascha@547: public RasterObject() {
sascha@547: }
sascha@547:
sascha@547: public RasterObject(
sascha@547: double mx, double bx,
sascha@547: double my, double by,
sascha@885: int columnIndex,
sascha@547: int rowIndex,
sascha@547: double [] rasterData,
sascha@547: int tileWidth,
sascha@547: int tileHeight
sascha@547: ){
sascha@547: this.mx = mx;
sascha@547: this.bx = bx;
sascha@547: this.my = my;
sascha@547: this.by = by;
tim@544: this.columnIndex = columnIndex;
sascha@547: this.rowIndex = rowIndex;
sascha@547: this.rasterData = rasterData;
sascha@547: this.tileWidth = tileWidth;
sascha@547: this.tileHeight = tileHeight;
tim@544: }
sascha@547:
sascha@547: public boolean contains(Coordinate coordinate) {
sascha@547: double px = mx*coordinate.x + bx;
sascha@547: double py = my*coordinate.y + by;
sascha@547: return px >= 0d && py >= 0d && px < tileWidth && py < tileHeight;
sascha@547: }
sascha@547:
sascha@547: public final double get(int posX, int posY) {
sascha@547: return rasterData[posY*tileWidth + posX];
sascha@547: }
sascha@547:
sascha@547: public int getTileWidth() {
sascha@547: return tileWidth;
sascha@547: }
sascha@547:
sascha@547: public int getTileHeight() {
sascha@547: return tileWidth;
sascha@547: }
sascha@547:
sascha@547: public int getColumnIndex() {
sascha@547: return columnIndex;
sascha@547: }
sascha@547:
sascha@547: public int getRowIndex() {
sascha@547: return rowIndex;
tim@544: }
sascha@885:
sascha@547: public double getValue(Coordinate coordinate) {
sascha@547: return getValue(coordinate, NEAREST_NEIGHBOR);
tim@544: }
tim@544:
sascha@548: public final double interpolateBilinear(double px, double py) {
sascha@548:
sascha@548: if (px < 0.5d) { // left border
sascha@548: if (py < 0.5d) { // upper left edge
sascha@548: return rasterData[0];
sascha@548: }
sascha@548: if (py > tileHeight-0.5d) { // lower left edge
sascha@548: return rasterData[rasterData.length-tileWidth];
sascha@548: }
sascha@548: // center left
sascha@548: int i = (int)(py -= 0.5d);
sascha@548: double t = py - i;
sascha@548: i *= tileWidth;
sascha@548: return (1d-t)*rasterData[i] + t*rasterData[i+tileWidth];
sascha@548: }
sascha@548:
sascha@548: if (px > tileWidth-0.5d) { // right border
sascha@548: if (py < 0.5d) { // upper right edge
sascha@548: return rasterData[tileWidth-1];
sascha@548: }
sascha@548: if (py > tileHeight-0.5d) { // lower right edge
sascha@548: return rasterData[rasterData.length-1];
sascha@548: }
sascha@548: // center right
sascha@548: int i = (int)(py -= 0.5d);
sascha@548: double t = py - i;
sascha@548: i = i*tileWidth + tileWidth-1;
sascha@548: return (1d-t)*rasterData[i] + t*rasterData[i+tileWidth];
sascha@548: }
sascha@548:
sascha@548: if (py < 0.5d) { // top border
sascha@548: int i = (int)(px -= 0.5d);
sascha@548: double t = px - i;
sascha@548: return (1d-t)*rasterData[i] + t*rasterData[i+1];
sascha@548: }
sascha@548:
sascha@548: if (py > tileHeight-0.5d) { // bottom border
sascha@548: int i = (int)(px -= 0.5d);
sascha@548: double t = px - i;
sascha@548: i += rasterData.length-tileWidth;
sascha@548: return (1d-t)*rasterData[i] + t*rasterData[i+1];
sascha@548: }
sascha@548:
sascha@548: // center
sascha@548: int i = (int)(py -= 0.5d);
sascha@548: int j = (int)(px -= 0.5d);
sascha@548: double ti = py - i;
sascha@548: double tj = px - j;
sascha@548:
sascha@548: int idx = i*tileWidth + j;
sascha@548:
sascha@548: double v1j1 = rasterData[idx];
sascha@548: double v2j1 = rasterData[idx+1];
sascha@548:
sascha@548: idx += tileWidth;
sascha@548:
sascha@548: double v1j2 = rasterData[idx];
sascha@548: double v2j2 = rasterData[idx+1];
sascha@548:
sascha@548: double v1 = (1d-tj)*v1j1 + tj*v2j1;
sascha@548: double v2 = (1d-tj)*v1j2 + tj*v2j2;
sascha@548:
sascha@548: return (1d-ti)*v1 + ti*v2;
sascha@548: }
sascha@548:
sascha@550: protected PolynomialSplineFunction [] createSplines() {
sascha@550: PolynomialSplineFunction [] splines =
sascha@550: new PolynomialSplineFunction[tileHeight];
sascha@550:
sascha@550: double [] x = new double[tileWidth];
sascha@550: double [] y = new double[tileWidth];
sascha@550:
sascha@550: for (int i = 0; i < tileWidth; ++i) {
sascha@550: x[i] = i + 0.5d;
sascha@550: }
sascha@550:
sascha@550: columns = new double[tileHeight];
sascha@550:
sascha@550: SplineInterpolator interpolator = new SplineInterpolator();
sascha@550:
sascha@550: for (int i = 0; i < tileHeight; ++i) {
sascha@550: int row = i*tileWidth;
sascha@550: for (int j = 0; j < tileWidth; ++j) {
sascha@550: y[j] = rasterData[row + j];
sascha@550: }
sascha@550: splines[i] = interpolator.interpolate(x, y);
sascha@550: columns[i] = i + 0.5d;
sascha@550: }
sascha@550:
sascha@550: return splines;
sascha@550: }
sascha@550:
sascha@550: protected synchronized PolynomialSplineFunction [] getSplines() {
sascha@550: if (splines == null) {
sascha@550: splines = createSplines();
sascha@550: }
sascha@550: return splines;
sascha@550: }
sascha@550:
sascha@550: protected double [] columnValues(double x) {
sascha@550: PolynomialSplineFunction [] splines = getSplines();
sascha@550: double [] y = new double[tileHeight];
sascha@550: try {
sascha@550: for (int i = 0; i < tileHeight; ++i) {
sascha@550: y[i] = splines[i].value(x);
sascha@550: }
sascha@550: }
sascha@550: catch (ArgumentOutsideDomainException aode) {
sascha@550: log.error(aode.getLocalizedMessage(), aode);
sascha@550: }
sascha@550: return y;
sascha@550: }
sascha@550:
sascha@550: private final double evaluateBicubicVertical(double x, double y) {
sascha@550: try {
sascha@550: SplineInterpolator interpolator = new SplineInterpolator();
sascha@550: double [] cs = columnValues(x);
sascha@550: PolynomialSplineFunction spline = interpolator.interpolate(
sascha@550: columns, cs);
sascha@550: return spline.value(y);
sascha@550: }
sascha@550: catch (ArgumentOutsideDomainException aode) {
sascha@550: log.error(aode.getLocalizedMessage(), aode);
sascha@550: }
sascha@550: return 0d;
sascha@550: }
sascha@550:
sascha@550: private final double evaluateBicubicHorizonal(int index, double x) {
sascha@550: try {
sascha@550: PolynomialSplineFunction spline = getSplines()[index];
sascha@550: return spline.value(x);
sascha@550: }
sascha@550: catch (ArgumentOutsideDomainException aode) {
sascha@550: log.error(aode.getLocalizedMessage(), aode);
sascha@550: }
sascha@550: return 0d;
sascha@550: }
sascha@550:
sascha@550: public double interpolateBicubic(double px, double py) {
sascha@550:
sascha@550: if (px <= 0.5d) { // left
sascha@550: if (py <= 0.5d) { // upper left
sascha@550: return rasterData[0];
sascha@550: }
sascha@550: if (py >= tileHeight-0.5d) {
sascha@550: return rasterData[rasterData.length-tileWidth];
sascha@550: }
sascha@550: return evaluateBicubicVertical(0.5d, py);
sascha@550: }
sascha@550:
sascha@550: if (px >= tileWidth-0.5d) { // right
sascha@550: if (py < 0.5d) { // upper right edge
sascha@550: return rasterData[tileWidth-1];
sascha@550: }
sascha@550: if (py > tileHeight-0.5d) { // lower right edge
sascha@550: return rasterData[rasterData.length-1];
sascha@550: }
sascha@550: return evaluateBicubicVertical(tileHeight-0.5d, py);
sascha@550: }
sascha@550:
sascha@550: if (py <= 0.5d) { // top
sascha@550: return evaluateBicubicHorizonal(0, px);
sascha@550: }
sascha@550:
sascha@550: if (py >= tileHeight-0.5d) { // bottom
sascha@550: return evaluateBicubicHorizonal(tileHeight-1, px);
sascha@550: }
sascha@550:
sascha@550: return evaluateBicubicVertical(px, py);
sascha@550: }
sascha@550:
sascha@547: public double getValue(Coordinate coordinate, int interpolationType) {
sascha@547:
sascha@547: double px = mx*coordinate.x + bx;
sascha@547: double py = my*coordinate.y + by;
sascha@547:
sascha@547: if (px < 0d || py < 0d || px >= tileWidth || py >= tileHeight) {
sascha@547: return Double.NaN;
sascha@547: }
sascha@547:
sascha@550: switch (interpolationType) {
sascha@550: case BILINEAR: return interpolateBilinear(px, py);
sascha@550: case BICUBIC : return interpolateBicubic (px, py);
sascha@547: }
sascha@547:
sascha@547: int posX = Math.min(tileWidth-1, (int)Math.round(px));
sascha@547: int posY = Math.min(tileHeight-1, (int)Math.round(py));
sascha@547:
sascha@547: return get(posX, posY);
sascha@547: }
sascha@549:
sascha@549: public static final int getInterpolationType(String key) {
sascha@549: if (key == null || (key = key.trim().toLowerCase()).length() == 0) {
sascha@549: return NEAREST_NEIGHBOR;
sascha@549: }
sascha@549:
sascha@549: if ("bilinear".equals(key)) {
sascha@549: return BILINEAR;
sascha@549: }
sascha@549:
sascha@550: if ("bicubic".equals(key)) {
sascha@550: return BICUBIC;
sascha@550: }
sascha@550:
sascha@549: return NEAREST_NEIGHBOR;
sascha@549: }
tim@544: }