当前位置: 首页 > news >正文

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

读取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,避免除以0double 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;}}

相关文章:

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

读取netCdf的依赖 <dependency><groupId>ucar</groupId><artifactId>netcdfAll</artifactId><version>5.5.3</version><scope>system</scope><exclusions><exclusion><groupId>org.slf4j</groupId…...

数据结构——八大排序

一.排序的概念和其应用 1.1排序的概念 排序&#xff1a;排列或排序是将一组数据按照一定的规则或顺序重新组织的过程&#xff0c;数据既可以被组织成递增顺序&#xff08;升序&#xff09;&#xff0c;或者递减顺序&#xff08;降序&#xff09;。稳定性&#xff1a;假定在待…...

【Unity】RPG2D龙城纷争(十九)流程与UI界面(终章)

更新日期:2024年8月1日。 项目源码:第五章发布(正式开始游戏逻辑的章节) 索引 简介一、游戏流程1.初始化流程2.开始流程3.关卡流程4.关卡结束流程5.启用所有流程二、UI界面逻辑1.开始界面2.存档界面3.关卡界面DataRegion 数据显示逻辑区域RoundRegion 回合逻辑区域RoleMenu…...

C#类和结构体的区别

1、类class是引用类型&#xff0c;多个引用类型变量的值会互相影响。存储在堆&#xff08;heap&#xff09;上 2、结构体struct是值类型&#xff0c;多个值类型变量的值不会互相影响。存储在栈&#xff08;stack&#xff09;上 类结构关键字classstruct类型引用类型值类型存储…...

【RabbitMQ】RabbitMQ持久化

一、简介 RabbitMQ的持久化机制是一种确保数据在RabbitMQ服务重启或异常情况下不会丢失的重要特性。RabbitMQ的持久化主要包括三个方面的内容&#xff1a;交换器的持久化、队列的持久化、消息的持久化。 二、交换器的持久化 1、实现方式 在RabbitMQ中&#xff0c;实现交换器…...

算法刷题笔记 Kruskal算法求最小生成树(详细算法介绍,详细注释C++代码实现)

文章目录 题目描述基本思路实现代码 题目描述 给定一个n个点m条边的无向图&#xff0c;图中可能存在重边和自环&#xff0c;边权可能为负数。求最小生成树的树边权重之和&#xff0c;如果最小生成树不存在则输出impossible。 最小生成树的概念&#xff1a;给定一张边带权的无向…...

5年经验的软件测试人员,碰到这样的面试题居然会心虚......

我们这边最近的面试机会比较多&#xff0c;但是根据他们的反馈&#xff0c;结束后大部分都没音信了&#xff0c;因为现在企业面试问的非常多&#xff0c;范围非常广&#xff0c;而且开放性的问题很多&#xff0c;很多人即便面试前刷了成百上千道面试题&#xff0c;也很难碰到一…...

C#进阶-轻量级ORM框架Dapper的使用教程与原理详解

本文详细介绍了Dapper在C#中的使用方法&#xff0c;包括Dapper的基本概念、与其他持久层框架的比较、基本语法和高级语法的使用&#xff0c;并通过实例讲解了如何在项目中集成和使用Dapper。Dapper以其高效的性能和简洁的API受到开发者的青睐&#xff0c;适用于各种数据库操作需…...

Windows图形界面(GUI)-MFC-C/C++ - 编辑框(Edit Control) - CEdit

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 编辑框(Edit Control) - CEdit 基本概念 成员函数 示例代码 编辑框(Edit Control) - CEdit 基本概念 编辑框&#xff08;Edit Control&#xff09;是一个允许用户输入和编辑文本的窗…...

网络安全防御【IPsec VPN搭建】

目录 一、实验拓扑图 二、实验要求 三、实验思路 四、实验步骤&#xff1a; 修改双机热备的为主备模式&#xff1a; 2、配置交换机LSW6新增的配置&#xff1a; 3、防火墙&#xff08;FW4&#xff09;做相关的基础配置&#xff1a; 4、搭建IPsec VPN通道 &#xff08;1…...

java环境配置与tomcat的配置

1、java环境配置 一、JDK下载 访问Oracle官网&#xff1a; 前往Oracle官网&#xff08;Oracle | Cloud Applications and Cloud Platform&#xff09;&#xff0c;在首页的顶部菜单中选择“Resources” > “Downloads” > “Java” > “JDK”。注意&#xff1a;Orac…...

OD C卷 - 来自异国的客人/幸运数字

来自异国的客人/幸运数字&#xff08;100&#xff09; 输入描述&#xff1a; 输入k,n,m k表示物品价值&#xff08;十进制&#xff09; k>0 n表示幸运数字, n > 0 m表示异国采用的进制&#xff1b;m > 1 n < m 输出描述&#xff1a; 输出幸运数字的个数&#xff0…...

C++ | 动态内存管理 new、delete (用法、底层)详解

目录 简单回顾C语言动态内存管理 new、delete的用法 内置类型 new delete 自定义类型 new、delete底层讲解&#xff08;重要&#xff09; operator new 与 operator delete 定位 new 结语 简单回顾C语言动态内存管理 在C语言的学习阶段 我们接触到了三个能在堆上开辟…...

【C语言】结构体内存布局解析——字节对齐

&#x1f984;个人主页:小米里的大麦-CSDN博客 &#x1f38f;所属专栏:https://blog.csdn.net/huangcancan666/category_12718530.html &#x1f381;代码托管:黄灿灿 (huang-cancan-xbc) - Gitee.com ⚙️操作环境:Visual Studio 2022 目录 一、引言 二、什么是字节对齐&…...

模型表达方式

目录 一、模型表达概述 二、模型精确表达 2.1 几何表示 (Geometrical Representation) 三、模型非精确表达 3.1 网格表示 (Mesh Representation) 3.2 体素表示 (Voxel Representation) 一、模型表达概述 模型的表达方式多种多样,选择适合的表达方式取决于具体应用场景和…...

校园课程助手【4】-使用Elasticsearch实现课程检索

本节将介绍本项目的查询模块&#xff0c;使用Elasticsearch又不是查询接口&#xff0c;具体流程如图所示&#xff08;如果不了解Elasticsearch可以使用sql语句进行查询&#xff09;&#xff1a; 这里是两种方法的异同点&#xff1a; Mysql&#xff1a;擅长事务类型操作&#…...

经典运维面试题

1、Linux常见的日志文件都有哪些&#xff0c;各自的用途&#xff1f;日志轮询配置文件在哪里&#xff1f;欢迎界面配置文件在哪里&#xff1f; /var/log/messages #内核及公共消息日志/var/log/cron #计划任务日志/var/log/dmesg #系统引导日志/var/log/malilog #邮件系…...

别再盲目推广了!Xinstall助你开启App线下推广新篇章

在这个数字化飞速发展的时代&#xff0c;App已经成为我们生活中不可或缺的一部分。然而&#xff0c;App市场的竞争也日益激烈&#xff0c;如何让你的App在众多竞争者中脱颖而出&#xff0c;成为每个推广者必须面对的问题。今天&#xff0c;就让我们一起探讨一下App线下推广的痛…...

大厂linux面试题攻略五之数据库管理

一、数据库管理-MySQL语句 0.MySQL基本语句&#xff1a; 1.SQL语句-增 创建xxx用户&#xff1a; mysql>create user xxx % indentified by 123456; xxx表示用户名 %b表示该用户用来连接数据库的方式&#xff08;远程或本地连接&#xff09; indentified by 123456设置密码…...

【pytorch】模型集成

在集成学习中&#xff0c;我们会训练多个模型&#xff08;通常称为「弱学习器」&#xff09;解决相同的问题&#xff0c;并将它们结合起来以获得更好的结果。最重要的假设是&#xff1a;当弱模型被正确组合时&#xff0c;我们可以得到更精确和/或更鲁棒的模型。 常用的模型集成…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...