JAVA读取netCdf文件并绘制热力图

28 篇文章 0 订阅

读取netCdf的依赖

		<dependency>
            <groupId>ucar</groupId>
            <artifactId>netcdfAll</artifactId>
            <version>5.5.3</version>
            <scope>system</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>spi</artifactId>
                </exclusion>
            </exclusions>
            <systemPath>${basedir}/libs/netcdfAll-5.5.3.jar</systemPath>
        </dependency>

读取文件入库

import cn.iscas.eneity.*;
import cn.iscas.picture.HeatPictureGenerator;
import cn.iscas.util.DateAddition;
import cn.iscas.util.GzipCompressUtil;
import cn.iscas.util.LogUtil;
import cn.iscas.util.PropertiesUtil;
import com.google.common.collect.ImmutableList;
import com.iscas.datasong.client.DataSongClient;
import com.iscas.datasong.client.DataSongHttpClient;
import com.iscas.datasong.client.domain.DataSongSearchResult;
import com.iscas.datasong.lib.common.DataSongException;
import com.iscas.datasong.lib.request.SearchDataRequest;
import com.iscas.datasong.lib.request.search.builder.ConditionBuilder;
import com.iscas.datasong.lib.request.search.condition.search.TermSearchCondition;
import com.iscas.datasong.lib.util.DataSongJsonUtils;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;
import java.io.IOException;
import java.util.*;

public static void salinitySaveDataSong(String fileName) throws IOException, DataSongException {
        NetcdfFile file = NetcdfFile.open(fileName);
        Map map = new HashMap<>();
        ImmutableList<Variable> variables = file.getVariables();
        for (Variable var : variables) {
            String varName = var.getFullName();
            Object o = var.read().copyToNDJavaArray();
            map.put(varName, o);
        }
        List<Salinity> salinityList = new ArrayList<>();
        int[] time = (int[]) map.get("time");
        float[] lev = (float[]) map.get("lev");
        float[][][][] ss = (float[][][][]) map.get("ss");
        float[] lat = (float[]) map.get("lat");
        float[] lon = (float[]) map.get("lon");
        for (int i = 0; i < time.length; i++) {
            for (int j = 0; j < lev.length; j++) {
                Salinity salinity = new Salinity();
                salinity.setTime(DateAddition.addDays365(time[i]));
                salinity.setLev(lev[j]);
                salinity.setLat(Arrays.toString(lat));
                salinity.setLon(Arrays.toString(lon));
                salinity.setNetCdfPath(fileName);
                //压缩数据
                String compress = GzipCompressUtil.compress(DataSongJsonUtils.toJson(ss[i][j]));
                salinity.setSs(compress);
                salinityList.add(salinity);
            }
        }
        DataSongClient dataSongClient = DataSongHttpClient.getInstance(dataSongIp, dataSongPort);
        dataSongClient.setDatabaseName(dataSongDatabase);
        dataSongClient.getDataService().batchSaveData(salinityList).toString();
        LogUtil.debug(fileName + "入库解析完成");
    }

读取入库数据

public static void salinityGeneratorPicture(String netCdfPath) throws DataSongException {
        DataSongClient dataSongClient = DataSongHttpClient.getInstance(dataSongIp,dataSongPort);
        dataSongClient.setDatabaseName(dataSongDatabase);
        TermSearchCondition levTermSearchCondition = ConditionBuilder.termCondition("netCdfPath", netCdfPath);
        SearchDataRequest searchDataRequest = new SearchDataRequest();
        searchDataRequest.setSearch(levTermSearchCondition);
        DataSongSearchResult<Salinity> salinityDataSongSearchResult = dataSongClient.getDataService().searchData(Salinity.class, searchDataRequest);
        List<Salinity> items = salinityDataSongSearchResult.getItems();
        String ss = "";
        String lat = "";
        String lon = "";
        String pictureName = "";
        for (Salinity salinity : items) {
            salinity.setSs(GzipCompressUtil.decompress(salinity.getSs()));
            ss = salinity.getSs();
            lat = salinity.getLat();
            lon = salinity.getLon();
            pictureName = "salinity" + "_" + salinity.getTime() + "_" + Float.valueOf(salinity.getLev()).intValue();
            float[] latitudes = DataSongJsonUtils.fromJson(lat, float[].class);
            float[] longitudes = DataSongJsonUtils.fromJson(lon, float[].class);
            double[][] values = DataSongJsonUtils.fromJson(ss, double[][].class);
            //画图
            String pictureFile = HeatPictureGenerator.pictureGenerator(pictureName, latitudes, longitudes, values);
            if (pictureFile != null) {
                salinity.setPicturePath(pictureFile);
                //写入生成的图片位置
                dataSongClient.getDataService().updateData(salinity);
                salinity.setSs("");
                LogUtil.debug("记录一条"+salinity);
            }
        }
    }

绘制热力图

import javax.imageio.ImageIO;
import cn.iscas.util.LogUtil;
import cn.iscas.util.PropertiesUtil;
import com.iscas.datasong.lib.common.DataSongException;
import java.awt.*;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
   
     * 生成热力图(tiff,png格式)
     * 示例的经度、纬度和对应的值
     *
     * @param latitudes  float[] longitudes = {100.0f, 200.0f, 300.0f, 400.0f, 500.0f};
     * @param longitudes float[] latitudes = {100.0f, 150.0f, 200.0f, 250.0f, 300.0f};
     * @param values     double[][] values = {
     *                   {0.2, 0.4, 0.6, 0.8, 1.0},
     *                   {0.3, 0.5, 0.7, 0.9, 1.1},
     *                   {0.4, 0.6, 0.8, 1.0, 1.2},
     *                   {0.5, 0.7, 0.9, 1.1, 1.3},
     *                   {0.6, 0.8, 1.0, 1.2, 1.4}
     *                   };
     */
    public static String pictureGenerator(String pictureName, float[] latitudes, float[] longitudes, double[][] values) {
        // 定义图像宽高
//        int width = 1600;
//        int height = 1300;
        int messageHeight = 100;
        int width = 1486;
        int height = 910 + messageHeight;
        // 创建BufferedImage对象
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 获取Graphics2D对象以便绘制
        Graphics2D g2d = image.createGraphics();
        // 设置背景颜色为白色
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, width, height);
        // 找到最小值和最大值
        double minValue = Double.MAX_VALUE;
        double maxValue = Double.MIN_VALUE;
        for (int i = 0; i < latitudes.length; i++) {
            for (int j = 0; j < longitudes.length; j++) {
                if (values[i][j] != 1.0E35 && !Double.isNaN(values[i][j]) && !Double.isInfinite(values[i][j])) {
                    if (values[i][j] < minValue) {
                        minValue = values[i][j];
                    }
                    if (values[i][j] > maxValue) {
                        maxValue = values[i][j];
                    }
                }
            }
        }
        // 确定经度和纬度的最大最小值,用于缩放坐标
        float minLongitude = Float.MAX_VALUE;
        float maxLongitude = Float.MIN_VALUE;
        float minLatitude = Float.MAX_VALUE;
        float maxLatitude = Float.MIN_VALUE;
        for (float longitude : longitudes) {
            if (longitude < minLongitude) minLongitude = longitude;
            if (longitude > maxLongitude) maxLongitude = longitude;
        }
        for (float latitude : latitudes) {
            if (latitude < minLatitude) minLatitude = latitude;
            if (latitude > maxLatitude) maxLatitude = latitude;
        }
        // 绘制数据点
        for (int i = 0; i < latitudes.length; i++) {
            for (int j = 0; j < longitudes.length; j++) {
                // 缩放坐标
                int x = Math.round((longitudes[j] - minLongitude) / (maxLongitude - minLongitude) * (width - 1));
//                int y = Math.round((latitudes[i] - minLatitude) / (maxLatitude - minLatitude) * (height - 1));
                int y = Math.round(height - messageHeight - 1 - ((latitudes[i] - minLatitude) / (maxLatitude - minLatitude) * (height - messageHeight - 1)));
                // 剔除无效值
                if (values[i][j] == 1.0E35 || Double.isNaN(values[i][j]) || Double.isInfinite(values[i][j])) {
                    // 使用一个默认值或跳过这个数据点
                    g2d.setColor(Color.GRAY);
                    g2d.fillRect(x, y, 2, 2);
                } else {
                    // 归一化处理
                    float value = (float) ((values[i][j] - minValue) / (maxValue - minValue));
                    // 根据值计算颜色
//                Color color = new Color(value, 0.0f, 1.0f - value); // 从蓝到红的渐变色
                    // 将 value 从 [0, 1] 映射到 [0, 255] 的色调值(因为色调是 0-360 度的循环,但我们可以将其转换为 0-255 的范围)
                    // 将 value 从 [0, 1] 映射到 [0, 240] 的色调值(因为蓝色是 240 度,红色是 0 度)
                    float hue = (float) (240.0 - value * 240.0); // 从蓝色(240)渐变到红色(0)
                    // 你可以设置固定的饱和度和亮度值,或者根据需要进行调整
                    float saturation = 1.0f; // 完全饱和
                    float brightness = 1.0f; // 最大亮度
                    // 使用 HSB 值创建颜色
                    Color color = Color.getHSBColor(hue / 360f, saturation, brightness);
                    g2d.setColor(color);
                    g2d.fillOval(x, y, 2, 2); // 绘制圆形点
                }
            }
        }
        // 绘制颜色条
        int colorbarHeight = 50; // 颜色条的高度
        int colorbarY = height - colorbarHeight; // 颜色条的位置
        int colorbarWidth = width - 200; // 颜色条的长度与图片宽度相同
        // 计算每个像素代表的值,注意减1,避免除以0
        double valuePerPixel = (maxValue - minValue) / (colorbarWidth - 1);
        // 绘制颜色条
        g2d.setColor(Color.BLACK); // 假设背景是黑色
        g2d.fillRect(0 + 80, colorbarY, colorbarWidth, colorbarHeight);
        for (int b = 0; b < colorbarWidth; b++) {
            double currentValue = minValue + b * valuePerPixel;
            // 计算色调值从蓝色(240)渐变到红色(0)
            float hueValue = 240f - (float) ((currentValue - minValue) / (maxValue - minValue) * 240);
            // 创建颜色
            Color color = Color.getHSBColor(hueValue / 360f, 1.0f, 1.0f); // 使用240来归一化hue
            // 设置颜色并绘制像素块
            g2d.setColor(color);
            g2d.fillRect(b + 80, colorbarY, 1, colorbarHeight);
        }
        // 绘制刻度线和标签
        int tickSpacing = colorbarWidth / 10; // 假设我们想要5个刻度线
        int tickLength = 10;
        int labelSpacing = tickSpacing; // 假设标签之间的间隔是刻度线间隔
        String format = "%.2f"; // 设置值的格式,这里保留两位小数
        for (int x = 0; x <= colorbarWidth; x += tickSpacing) {
            // 绘制刻度线
            g2d.setColor(Color.BLACK);
            g2d.drawLine(x + 80, colorbarY, x + 80, colorbarY + tickLength);
            // 计算当前刻度对应的值
            double currentValue = minValue + x * valuePerPixel;
            // 只在特定的间隔上绘制标签
            if (x % labelSpacing == 0) {
                // 绘制标签
                String label = String.format(format, currentValue);
                FontMetrics fm = g2d.getFontMetrics();
                int labelX = x - fm.stringWidth(label) / 2 + 80;
                int labelY = colorbarY + colorbarHeight + 15; // 设置标签的y坐标
                g2d.setColor(Color.BLACK);
                g2d.setFont(new Font("Default", Font.PLAIN, 20));
                g2d.drawString(label, labelX, labelY - 30);
            }
        }
        // 绘制标签
        g2d.setColor(Color.BLACK);
        g2d.drawString("Max: " + maxValue + "        " + "Min: " + minValue, 10, colorbarY + colorbarHeight - 60);
        // 释放Graphics2D资源
        g2d.dispose();
        // 保存图像为TIFF或者PNG文件,修改pathName和formatName即可
        try {
            File output = new File(picturePath + pictureName + ".tiff");
            ImageIO.write(image, "TIFF", output);
            LogUtil.debug("PNG图像已成功保存到 " + output.getAbsolutePath());
            return output.getAbsolutePath();
        } catch (IOException e) {
            e.printStackTrace();
            LogUtil.error("图片生成异常" + LogUtil.getStackTraceAsString(e));
            return null;
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值