海洋数值预报里面,数据格式一般都为nc文件格式,存储的为科学数据,这些数据都是网格化的数据。由于有些网格点是陆地,如果用图片方式去展示流场的化,合理的方式是将这些陆地剔除。由于一些客户端软件可以做到,这里不考虑。这里提供两个软件可供参考。
一个是jzy3d , 里面使用ContourPictureGenerator可生成想要的等值面,返回的为BufferedImage,当然流场方向还需自己处理。
另外一个是wcontour,可生成等值面,需要自己生成BufferedImage,再利用Contour.tracingStreamline获取流场曲线,再自己在BufferedImage上绘制。
@Override
protected Object wrapperRaw(CacheDataStore dataStore) {
double[] lons = Arrays.copyOf(dataStore.windLons, dataStore.windLons.length);
double[] lats = Arrays.copyOf(dataStore.windLats, dataStore.windLats.length);
// double[] lf, double[] rt,
double[] lf = getLfAndRtPts(lons, lats).get(0);
double[] rt = getLfAndRtPts(lons, lats).get(1);
// int rows, int cols, double[] contourVals,
int rows = lons.length;
int cols = lats.length;
double[][] data = genNorm(dataStore.wind_u, dataStore.wind_v, lons.length, lats.length, 10000, _undefData);
lons = initArr(rows);
lats = initArr(cols);
List lines = Contour.tracingStreamline(maskTable(dataStore.wind_u, 10000, _undefData),
maskTable(dataStore.wind_v, 10000, _undefData),
lons,
lats,
_undefData, 1);
BufferedImage img = ImgContourUtils.drawContourImage(maskTableEq(data, Math.abs(_undefData), Double.NaN), lons.length, lats.length, "");
ImgContourUtils.drawLines(img, lines);
//ImgContourUtils.drawArc(img, dataStore.wind_u, dataStore.wind_v, lons.length, lats.length, _undefData);
ImgContourUtils.saveImage(img, "d:\\temp\\flow2.png");
return img;
}
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DirectColorModel;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.jzy3d.colors.Color;
import org.jzy3d.colors.ColorMapper;
import org.jzy3d.colors.colormaps.ColorMapRainbow;
import org.jzy3d.contour.AbstractContourGenerator;
import org.jzy3d.contour.DefaultContourColoringPolicy;
import org.jzy3d.contour.IContourColoringPolicy;
import org.jzy3d.contour.IContourPictureGenerator;
import org.jzy3d.maths.Coord3d;
import org.jzy3d.maths.Range;
import org.jzy3d.plot3d.builder.Mapper;
import org.jzy3d.plot3d.builder.concrete.OrthonormalTessellator;
import org.jzy3d.plot3d.primitives.Shape;
import wContour.Global.PointD;
import wContour.Global.PolyLine;
public class ImgContourUtils {
public static BufferedImage getContourFilledImage(double[][] data, int m, int n, String path) {
OrthonormalTessellator tesselator = new OrthonormalTessellator();
// bottom, top, mid surface
Shape surface = (Shape) tesselator.build(toCoordList(data, n, m));
ColorMapper localColorMapper = new ColorMapper(new ColorMapRainbow(), surface.getBounds().getZmin(), surface.getBounds().getZmax(), new Color(1.0F, 1.0F, 1.0F, 1.0F));
System.out.println("max=" + surface.getBounds().getZmax());
System.out.println("min=" + surface.getBounds().getZmin());
CustomMapperContourPictureGenerator a = new CustomMapperContourPictureGenerator(new CustomMapper(data, n, m), new Range(0, n), new Range(0, m));
IContourColoringPolicy policy = new DefaultContourColoringPolicy(localColorMapper);
// BufferedImage img = a.getContourImage(policy, n, m, 10);
// BufferedImage img = a.getFilledContourImage(policy, n, m, new
// double[]{-10,30,40,50,60,70,80,90});
BufferedImage img = a.getFilledContourImage(policy, n, m, 10);
System.out.println(img);
updateAlpha(m, n, data, Double.NaN, img);
saveImage(img, path);
return img;
}
public static BufferedImage drawContourImage(double[][] data, int m, int n, String path) {
OrthonormalTessellator tesselator = new OrthonormalTessellator();
Shape surface = (Shape) tesselator.build(toCoordList(data, n, m));
ColorMapper localColorMapper = new ColorMapper(new ColorMapRainbow(), surface.getBounds().getZmin(), surface.getBounds().getZmax(), new Color(1.0F, 1.0F, 1.0F, 1.0F));
System.out.println("max=" + surface.getBounds().getZmax());
System.out.println("min=" + surface.getBounds().getZmin());
CustomMapperContourPictureGenerator a = new CustomMapperContourPictureGenerator(new CustomMapper(data, n, m), new Range(0, n), new Range(0, m));
IContourColoringPolicy policy = new DefaultContourColoringPolicy(localColorMapper);
BufferedImage img = a.getFilledContourImage(policy, n, m, 10);
updateAlpha(m, n, data, Double.NaN, img);
return img;
}
public static class CustomMapper extends Mapper {
private double[][] data;
private int m;
private int n;
public CustomMapper(double[][] data, int m, int n) {
this.data = data;
this.m = m;
this.n = n;
}
public final double f(double x, double y) {
if (x >= m || x < 0 || y >= n || y < 0) {
// System.out.println(x + "," + y);
}
return this.data[(int) Math.min(data.length - 1, Math.max(0, x))][(int) Math.min(data[0].length - 1, Math.max(0, y))];
}
}
public static List toCoordList(double[][] z, int m, int n) {
List ret = new ArrayList();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
ret.add(new Coord3d(i, j, z[i][j]));
}
}
return ret;
}
public static void saveImage(BufferedImage img, String path) {
try {
File outputfile = new File(path);
ImageIO.write(img, "png", outputfile);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static BufferedImage drawLines(int xRes, int yRes, double[][] vals, double _undef, List polyLines) {
BufferedImage img = buildImage(xRes, yRes, vals, _undef);
drawLines(img, polyLines);
return img;
}
public static void drawLines(BufferedImage img, List polyLines) {
Graphics2D g2d = img.createGraphics();
List xs = toIntArrsX(polyLines, img.getWidth());
List ys = toIntArrsY(polyLines, img.getHeight());
g2d.setColor(java.awt.Color.blue);
for (int i = 0; i < xs.size(); i = i + 12) {
g2d.drawPolyline(xs.get(i), ys.get(i), xs.get(i).length);
int[] xPoints = xs.get(i);
int[] yPoints = ys.get(i);
int alen = xs.get(i).length;
// break;
{
int len = 12;
for (int j = 0; j < alen; j = j + 50) {
if (j > 0 && j < alen - 2 && j % len == 0) {
// Draw arraw
java.awt.Point aP = new java.awt.Point(xPoints[j], yPoints[j]);
java.awt.Point bPoint = new java.awt.Point(xPoints[j + 1], yPoints[j + 1]);
double U = bPoint.x - aP.x;
double V = bPoint.y - aP.y;
double angle = Math.atan((V) / (U)) * 180 / Math.PI;
angle = angle + 90;
if (U < 0) {
angle = angle + 180;
}
if (angle >= 360) {
angle = angle - 360;
}
if (Double.isNaN(angle)) {
continue;
}
java.awt.Point eP1 = new java.awt.Point();
double aSize = 8;
eP1.x = (int) (aP.x - aSize * Math.sin((angle + 20.0) * Math.PI / 180));
eP1.y = (int) (aP.y + aSize * Math.cos((angle + 20.0) * Math.PI / 180));
g2d.drawLine(aP.x, aP.y, eP1.x, eP1.y);
eP1.x = (int) (aP.x - aSize * Math.sin((angle - 20.0) * Math.PI / 180));
eP1.y = (int) (aP.y + aSize * Math.cos((angle - 20.0) * Math.PI / 180));
g2d.drawLine(aP.x, aP.y, eP1.x, eP1.y);
// System.out.println(Math.abs(aP.x - eP1.x) + "," +
// Math.abs(aP.y - eP1.y));
}
}
}
}
g2d.dispose();
}
public static void drawArc(BufferedImage img, double[][] windU, double[][] windV, int m, int n, double mask) {
Graphics2D g2d = img.createGraphics();
g2d.setColor(java.awt.Color.yellow);
for (int i = 0; i < m; i = i + 24) {
for (int j = 0; j < n; j = j + 24) {
double u = windU[j][i];
double v = windV[j][i];
if (Double.isNaN(u) || Double.isNaN(v) || Math.abs(u) == Math.abs(mask) || Math.abs(v) == Math.abs(mask)) {
continue;
}
g2d.drawString(getStringByAngle(u, v), i, n - 1 -j);
}
}
g2d.dispose();
}
public static void drawArc(BufferedImage img, double[][] data, int m, int n, double mask) {
Graphics2D g2d = img.createGraphics();
g2d.setColor(java.awt.Color.yellow);
for (int i = 0; i < m; i = i + 24) {
for (int j = 0; j < n; j = j + 24) {
double u = data[j][i];
if (Double.isNaN(u) || Math.abs(u) == Math.abs(mask) ) {
continue;
}
u = 360 -data[j][i] * 0.1;
g2d.drawString(getAngleByVal(u), i, n - 1 -j);
}
}
g2d.dispose();
}
private static String getStringByAngle(double x, double y) {
if (x > 0) {
if (y == 0) {
return "→";
} else if (y > 0) {
return "↗";
}
return "↘";
} else if (x < 0) {
if (y == 0) {
return "←";
} else if (y > 0) {
return "↖";
}
return "↙";
} else {
if (y >= 0) {
return "↑";
}
return "↓";
}
}
static String getAngleByVal(double v){
if( v < 0){
return "→";
}
if( v >= 0 && v < 45){
return "→";
}else if( v <= 90){
if( v == 90){
return "↑";
}
return "↗";
}else if( v <= 180){
if( v == 180){
return "←";
}
return "↖";
}else if( v <= 270){
if( v == 270){
return "↓";
}
return "↙";
}else{
if( v == 360){
return "→";
}
return "↘";
}
}
static List toIntArrsX(List polyLines, int max) {
List xs = new ArrayList();
for (int i = 0; i < polyLines.size(); i++) {
PolyLine pl = polyLines.get(i);
List is = new ArrayList();
for (PointD d : pl.PointList) {
int x = (int) Math.round(d.X);
if (x >= max) {
x = max - 1;
}
is.add(x);
}
xs.add(toIntArr(is));
}
return xs;
}
static List toIntArrsY(List polyLines, int max) {
List ys = new ArrayList();
for (int i = 0; i < polyLines.size(); i++) {
PolyLine pl = polyLines.get(i);
List is = new ArrayList();
for (PointD d : pl.PointList) {
int y = (int) Math.round(d.Y);
y = max - 1 - y;
if (y >= max) {
y = max - 1;
}
if (y < 0) {
y = 0;
}
is.add(y);
}
ys.add(toIntArr(is));
}
return ys;
}
static int[] toIntArr(List iList) {
int[] xs = new int[iList.size()];
for (int i = 0; i < iList.size(); i++) {
xs[i] = iList.get(i);
}
return xs;
}
static int getLocalRgb(int rgb) {
DirectColorModel dcm = (DirectColorModel) ColorModel.getRGBdefault();
// DirectColorModel类用来将ARGB值独立分解出来
int red = dcm.getRed(rgb);
int green = dcm.getGreen(rgb);
int blue = dcm.getBlue(rgb);
int alpha;
if (red == 255 && blue == 255 && green == 255) {// 如果像素为白色,则让它透明
alpha = 0;
} else {
alpha = 0;
}
return alpha << 24 | red << 16 | green << 8 | blue;
}
static BufferedImage buildImage(int xRes, int yRes, double[][] vals, double _undef) {
BufferedImage image = new BufferedImage(yRes, xRes, BufferedImage.TYPE_4BYTE_ABGR);
for (int x = 0; x < yRes; x++) {
for (int y = 0; y < xRes; y++) {
if (vals[y][x] == _undef) {
int rgb = -123;
rgb = getLocalRgb(rgb);
image.setRGB(x, y, rgb);
} else {
image.setRGB(x, y, java.awt.Color.lightGray.getRGB());
}
}
}
return image;
}
static BufferedImage updateAlpha(int xRes, int yRes, double[][] vals, double _undef, BufferedImage img) {
for (int x = 0; x < xRes; x++) {
for (int y = 0; y < yRes; y++) {
if (vals[y][x] == _undef || Double.isNaN(vals[y][x])) {
int rgb = -16777088;
rgb = getLocalRgb(rgb);
img.setRGB(x, yRes - 1 - y, rgb);
} else {
}
}
}
return img;
}
public static BufferedImage rotateImg(int xRes, int yRes, BufferedImage image) {
BufferedImage imageRotate = new BufferedImage(yRes, xRes, BufferedImage.TYPE_4BYTE_ABGR);
for (int x = 0; x < yRes; x++) {
for (int y = 0; y < xRes; y++) {
imageRotate.setRGB(x, y, image.getRGB(y, yRes - 1 - x));
}
}
return imageRotate;
}
public static class CustomMapperContourPictureGenerator extends AbstractContourGenerator implements IContourPictureGenerator {
public static int PIXEL_NEIGHBOUR_THRESHOLD = 2;
public static float LINE_STRIP_WIDTH = 2.0F;
public static int MERGE_STRIP_DIST = 1;
protected Mapper mapper;
protected Range xrange;
protected Range yrange;
public CustomMapperContourPictureGenerator(Mapper mapper, Range xrange, Range yrange) {
this.mapper = mapper;
this.xrange = xrange;
this.yrange = yrange;
}
public double[][] getContourMatrix(int xRes, int yRes, int nLevels) {
return computeContour(xRes, yRes, nLevels);
}
public BufferedImage getContourImage(IContourColoringPolicy policy, int xRes, int yRes, int nLevels) {
double[][] contours = computeContour(xRes, yRes, nLevels);
return buildImage(xRes, yRes, contours, policy);
}
public BufferedImage getContourImage(IContourColoringPolicy policy, int xRes, int yRes, double[] sortedLevels) {
double[][] contours = computeContour(xRes, yRes, sortedLevels);
return buildImage(xRes, yRes, contours, policy);
}
public BufferedImage getFilledContourImage(IContourColoringPolicy policy, int xRes, int yRes, int nLevels) {
double[][] contours = computeFilledContour(xRes, yRes, nLevels);
return buildImage(xRes, yRes, contours, policy);
}
public BufferedImage getFilledContourImage(IContourColoringPolicy policy, int xRes, int yRes, double[] sortedLevels) {
double[][] contours = computeFilledContour(xRes, yRes, sortedLevels);
return buildImage(xRes, yRes, contours, policy);
}
public BufferedImage getHeightMap(IContourColoringPolicy policy, int xRes, int yRes, int nLevels) {
double[][] contours = computeXYColors(xRes, yRes, nLevels);
return buildImage(xRes, yRes, contours, policy);
}
protected BufferedImage buildImage(int xRes, int yRes, double[][] contours, IContourColoringPolicy policy) {
BufferedImage image = new BufferedImage(xRes, yRes, BufferedImage.TYPE_4BYTE_ABGR);
for (int x = 0; x < xRes; x++) {
for (int y = 0; y < yRes; y++) {
image.setRGB(x, y, policy.getRGB(contours[x][y]));
}
}
// rotate
BufferedImage imageRotate = new BufferedImage(yRes, xRes, BufferedImage.TYPE_4BYTE_ABGR);
for (int x = 0; x < yRes; x++) {
for (int y = 0; y < xRes; y++) {
imageRotate.setRGB(x, y, image.getRGB(xRes - 1 - y, yRes - 1 - x));
}
}
return imageRotate;
}
protected double[][] computeFilledContour(int xRes, int yRes, double[] sortedLevels) {
double[][] matrix = new double[xRes][yRes];
computeHeightMatrix(matrix, xRes, yRes);
quantizeMatrix(matrix, sortedLevels);
return matrix;
}
protected void computeHeightMatrix(double[][] matrix, int xRes, int yRes) {
this.minValue = Double.MAX_VALUE;
this.maxValue = -1.7976931348623157E308D;
double xstep = this.xrange.getRange() / (xRes - 1);
double ystep = this.yrange.getRange() / (yRes - 1);
for (int xi = 0; xi < xRes; xi++) {
for (int yi = 0; yi < yRes; yi++) {
double x = this.xrange.getMin() + xi * xstep;
double y = this.yrange.getMin() + yi * ystep;
double value = this.mapper.f(x, y);
matrix[xi][(yRes - 1 - yi)] = value;
if (value < this.minValue) {
this.minValue = value;
}
if (value > this.maxValue) {
this.maxValue = value;
}
}
}
}
}
}