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排序的概念 排序:排列或排序是将一组数据按照一定的规则或顺序重新组织的过程,数据既可以被组织成递增顺序(升序),或者递减顺序(降序)。稳定性:假定在待…...
【Unity】RPG2D龙城纷争(十九)流程与UI界面(终章)
更新日期:2024年8月1日。 项目源码:第五章发布(正式开始游戏逻辑的章节) 索引 简介一、游戏流程1.初始化流程2.开始流程3.关卡流程4.关卡结束流程5.启用所有流程二、UI界面逻辑1.开始界面2.存档界面3.关卡界面DataRegion 数据显示逻辑区域RoundRegion 回合逻辑区域RoleMenu…...

C#类和结构体的区别
1、类class是引用类型,多个引用类型变量的值会互相影响。存储在堆(heap)上 2、结构体struct是值类型,多个值类型变量的值不会互相影响。存储在栈(stack)上 类结构关键字classstruct类型引用类型值类型存储…...
【RabbitMQ】RabbitMQ持久化
一、简介 RabbitMQ的持久化机制是一种确保数据在RabbitMQ服务重启或异常情况下不会丢失的重要特性。RabbitMQ的持久化主要包括三个方面的内容:交换器的持久化、队列的持久化、消息的持久化。 二、交换器的持久化 1、实现方式 在RabbitMQ中,实现交换器…...
算法刷题笔记 Kruskal算法求最小生成树(详细算法介绍,详细注释C++代码实现)
文章目录 题目描述基本思路实现代码 题目描述 给定一个n个点m条边的无向图,图中可能存在重边和自环,边权可能为负数。求最小生成树的树边权重之和,如果最小生成树不存在则输出impossible。 最小生成树的概念:给定一张边带权的无向…...

5年经验的软件测试人员,碰到这样的面试题居然会心虚......
我们这边最近的面试机会比较多,但是根据他们的反馈,结束后大部分都没音信了,因为现在企业面试问的非常多,范围非常广,而且开放性的问题很多,很多人即便面试前刷了成百上千道面试题,也很难碰到一…...

C#进阶-轻量级ORM框架Dapper的使用教程与原理详解
本文详细介绍了Dapper在C#中的使用方法,包括Dapper的基本概念、与其他持久层框架的比较、基本语法和高级语法的使用,并通过实例讲解了如何在项目中集成和使用Dapper。Dapper以其高效的性能和简洁的API受到开发者的青睐,适用于各种数据库操作需…...
Windows图形界面(GUI)-MFC-C/C++ - 编辑框(Edit Control) - CEdit
公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 编辑框(Edit Control) - CEdit 基本概念 成员函数 示例代码 编辑框(Edit Control) - CEdit 基本概念 编辑框(Edit Control)是一个允许用户输入和编辑文本的窗…...

网络安全防御【IPsec VPN搭建】
目录 一、实验拓扑图 二、实验要求 三、实验思路 四、实验步骤: 修改双机热备的为主备模式: 2、配置交换机LSW6新增的配置: 3、防火墙(FW4)做相关的基础配置: 4、搭建IPsec VPN通道 (1…...
java环境配置与tomcat的配置
1、java环境配置 一、JDK下载 访问Oracle官网: 前往Oracle官网(Oracle | Cloud Applications and Cloud Platform),在首页的顶部菜单中选择“Resources” > “Downloads” > “Java” > “JDK”。注意:Orac…...
OD C卷 - 来自异国的客人/幸运数字
来自异国的客人/幸运数字(100) 输入描述: 输入k,n,m k表示物品价值(十进制) k>0 n表示幸运数字, n > 0 m表示异国采用的进制;m > 1 n < m 输出描述: 输出幸运数字的个数࿰…...

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

【C语言】结构体内存布局解析——字节对齐
🦄个人主页:小米里的大麦-CSDN博客 🎏所属专栏:https://blog.csdn.net/huangcancan666/category_12718530.html 🎁代码托管:黄灿灿 (huang-cancan-xbc) - Gitee.com ⚙️操作环境:Visual Studio 2022 目录 一、引言 二、什么是字节对齐&…...
模型表达方式
目录 一、模型表达概述 二、模型精确表达 2.1 几何表示 (Geometrical Representation) 三、模型非精确表达 3.1 网格表示 (Mesh Representation) 3.2 体素表示 (Voxel Representation) 一、模型表达概述 模型的表达方式多种多样,选择适合的表达方式取决于具体应用场景和…...

校园课程助手【4】-使用Elasticsearch实现课程检索
本节将介绍本项目的查询模块,使用Elasticsearch又不是查询接口,具体流程如图所示(如果不了解Elasticsearch可以使用sql语句进行查询): 这里是两种方法的异同点: Mysql:擅长事务类型操作&#…...
经典运维面试题
1、Linux常见的日志文件都有哪些,各自的用途?日志轮询配置文件在哪里?欢迎界面配置文件在哪里? /var/log/messages #内核及公共消息日志/var/log/cron #计划任务日志/var/log/dmesg #系统引导日志/var/log/malilog #邮件系…...

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

大厂linux面试题攻略五之数据库管理
一、数据库管理-MySQL语句 0.MySQL基本语句: 1.SQL语句-增 创建xxx用户: mysql>create user xxx % indentified by 123456; xxx表示用户名 %b表示该用户用来连接数据库的方式(远程或本地连接) indentified by 123456设置密码…...
【pytorch】模型集成
在集成学习中,我们会训练多个模型(通常称为「弱学习器」)解决相同的问题,并将它们结合起来以获得更好的结果。最重要的假设是:当弱模型被正确组合时,我们可以得到更精确和/或更鲁棒的模型。 常用的模型集成…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 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”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
在树莓派上添加音频输入设备的几种方法
在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...

Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...