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】模型集成
在集成学习中,我们会训练多个模型(通常称为「弱学习器」)解决相同的问题,并将它们结合起来以获得更好的结果。最重要的假设是:当弱模型被正确组合时,我们可以得到更精确和/或更鲁棒的模型。 常用的模型集成…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...
若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
