/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* 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 GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package org.esa.snap.rcp.layermanager.layersrc.windfield;
import com.bc.ceres.binding.PropertyContainer;
import com.bc.ceres.binding.PropertySet;
import com.bc.ceres.glayer.Layer;
import com.bc.ceres.glayer.LayerType;
import com.bc.ceres.glayer.LayerTypeRegistry;
import com.bc.ceres.glayer.support.ImageLayer;
import com.bc.ceres.glevel.MultiLevelImage;
import com.bc.ceres.grender.Rendering;
import com.bc.ceres.grender.Viewport;
import org.esa.snap.core.datamodel.RasterDataNode;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.image.RenderedImage;
/**
* Experimental wind field layer. Given two band names for u,v, it could
* be generalized to any vector field layer.
*
* @author Norman Fomferra
* @since BEAM 4.6
*/
public class WindFieldLayer extends Layer {
private RasterDataNode windu;
private RasterDataNode windv;
private final Color[] palette;
private double maxLength = 10.0; // m/s
private int res = 16;
private float lineThickness = 2.0f;
public WindFieldLayer(PropertyContainer configuration) {
this(LayerTypeRegistry.getLayerType(WindFieldLayerType.class.getName()),
(RasterDataNode) configuration.getValue("windu"),
(RasterDataNode) configuration.getValue("windv"),
configuration);
}
public WindFieldLayer(LayerType layerType, RasterDataNode windu, RasterDataNode windv,
PropertySet configuration) {
super(layerType, configuration);
setName("Wind Speed");
this.windu = windu;
this.windv = windv;
palette = new Color[256];
for (int i = 0; i < palette.length; i++) {
palette[i] = new Color(i, i, i);
}
}
@Override
protected void renderLayer(Rendering rendering) {
final MultiLevelImage winduMLI = windu.getGeophysicalImage();
final MultiLevelImage windvMLI = windv.getGeophysicalImage();
final Viewport vp = rendering.getViewport();
final int level = ImageLayer.getLevel(winduMLI.getModel(), vp);
final AffineTransform m2i = winduMLI.getModel().getModelToImageTransform(level);
final AffineTransform i2m = winduMLI.getModel().getImageToModelTransform(level);
final Shape vbounds = vp.getViewBounds();
final Shape mbounds = vp.getViewToModelTransform().createTransformedShape(vbounds);
final Shape ibounds = m2i.createTransformedShape(mbounds);
final RenderedImage winduRI = winduMLI.getImage(level);
final RenderedImage windvRI = windvMLI.getImage(level);
final int width = winduRI.getWidth();
final int height = winduRI.getHeight();
final Rectangle irect = ibounds.getBounds().intersection(new Rectangle(0, 0, width, height));
if (irect.isEmpty()) {
return;
}
final AffineTransform m2v = vp.getModelToViewTransform();
final Graphics2D graphics = rendering.getGraphics();
graphics.setStroke(new BasicStroke(lineThickness));
final MultiLevelImage winduValidMLI = windu.getValidMaskImage();
final MultiLevelImage windvValidMLI = windv.getValidMaskImage();
final RenderedImage winduValidRI = winduValidMLI != null ? winduValidMLI.getImage(level) : null;
final RenderedImage windvValidRI = windvValidMLI != null ? windvValidMLI.getImage(level) : null;
final int x1 = res * (irect.x / res);
final int x2 = x1 + res * (1 + irect.width / res);
final int y1 = res * (irect.y / res);
final int y2 = y1 + res * (1 + irect.height / res);
final double[] ipts = new double[8];
final double[] mpts = new double[8];
final double[] vpts = new double[8];
final Rectangle pixelRect = new Rectangle(0, 0, 1, 1);
for (int y = y1; y <= y2; y += res) {
for (int x = x1; x <= x2; x += res) {
if (x >= 0 && x < width && y >= 0 && y < height) {
pixelRect.x = x;
pixelRect.y = y;
final boolean winduValid = winduValidRI == null || winduValidRI.getData(pixelRect).getSample(x, y, 0) != 0;
if (!winduValid) {
continue;
}
final boolean windvValid = windvValidRI == null || windvValidRI.getData(pixelRect).getSample(x, y, 0) != 0;
if (!windvValid) {
continue;
}
final double u = winduRI.getData(pixelRect).getSampleDouble(x, y, 0);
final double v = windvRI.getData(pixelRect).getSampleDouble(x, y, 0);
final double length = Math.sqrt(u * u + v * v);
final double ndx = length > 0 ? +u / length : 0;
final double ndy = length > 0 ? -v / length : 0;
final double ondx = -ndy;
final double ondy = ndx;
final double s0 = (length / maxLength) * res;
final double s1 = s0 - 0.2 * res;
final double s2 = 0.1 * res;
ipts[0] = x;
ipts[1] = y;
ipts[2] = x + s0 * ndx;
ipts[3] = y + s0 * ndy;
ipts[4] = x + s1 * ndx + s2 * ondx;
ipts[5] = y + s1 * ndy + s2 * ondy;
ipts[6] = x + s1 * ndx - s2 * ondx;
ipts[7] = y + s1 * ndy - s2 * ondy;
i2m.transform(ipts, 0, mpts, 0, 4);
m2v.transform(mpts, 0, vpts, 0, 4);
final int grey = Math.min(255, (int) Math.round(256 * length / maxLength));
graphics.setColor(palette[grey]);
graphics.draw(new Line2D.Double(vpts[0], vpts[1], vpts[2], vpts[3]));
graphics.draw(new Line2D.Double(vpts[4], vpts[5], vpts[2], vpts[3]));
graphics.draw(new Line2D.Double(vpts[6], vpts[7], vpts[2], vpts[3]));
}
}
}
}
}