Java项目实战记录:雷达数据渲染
Java项目实战记录:雷达数据渲染
业务背景
我之前已经成功使用Java语言解析了C++处理的雷达数据文件,并提取了其中的经纬度点数据。这些文件中的点数据量相当庞大,每个雷达产品文件通常包含超过20万个数据点,有时甚至更多。
目前面临着如何在前端展示这20万个点数据的挑战。有两种可行的解决方案:一种是直接将所有点数据传输到前端进行渲染;另一种则是在后端将点数据渲染成PNG图片,然后再将图片传输到前端展示。经过考虑,决定采用第二种方案,即在后端进行数据的渲染处理。这样不仅可以有效减轻前端的负担,提高数据处理和展示的效率,而且还可以通过优化渲染过程来提高整体的展示质量。
代码逻辑
这里,选用了GeoTools这个强大的开源Java库来执行雷达数据的渲染任务。GeoTools是专为地理空间数据处理设计的工具集,它提供了丰富的API来支持地理数据的读取、查询、分析和展示。利用GeoTools,能够高效地将雷达扫描得到的点数据转换为图像,并以PNG格式保存。
数据结构
雷达数据结构如下所示:
import lombok.Data;/*** 雷达点数据信息*/
@Data
public class LonLatData {private float lon; // 经度private float lat; // 纬度private double val; // 回拨强度 (等于value/100)
}
颜色渲染
根据回拨强度需要渲染不同颜色,定义一个Style样式,具体分类如下所示:
/* * 根据数值创建基于值的点样式。** @return 样式对象,根据不同的值范围应用不同的颜色。*/public static Style createValueBasedPointStyle() {// 创建一个黑色描边,宽度为1。Stroke stroke = styleFactory.createStroke(filterFactory.literal(Color.BLACK), filterFactory.literal(1));FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle();// 定义值的范围及其对应的颜色(十六进制表示)。Object[][] valueRanges = {{-5, Double.NEGATIVE_INFINITY, "#00acb8"},{0, -5, "#c1c0ac"},{10, 0, "#7a71fe"},{15, 10, "#1e26e3"},{20, 15, "#a6fcbb"},{25, 20, "#00ea15"},{30, 25, "#10932d"},{35, 30, "#fcf477"},{40, 35, "#c9c815"},{45, 40, "#8c8c12"},{50, 45, "#feadc0"},{55, 50, "#ff6370"},{60, 55, "#ee0244"},{65, 60, "#d48eff"},{Double.POSITIVE_INFINITY, 65, "#ab23ff"}};// 遍历每个值范围,为每个范围创建一个规则并添加到FeatureTypeStyle。for (Object[] range : valueRanges) {fts.rules().add(createRuleForRange(Double.parseDouble(range[1].toString()), Double.parseDouble(range[0].toString()), range[2].toString()));}// 创建样式并添加FeatureTypeStyle。Style style = styleFactory.createStyle();style.featureTypeStyles().add(fts);return style;}
MapContent加载数据并输出截图
首先创建了一个MapContent
对象,用它作为整个地图的内容载体。接着,创建了一个包含所有雷达数据点的图层,并为这些点指定了样式,这样做旨在通过视觉上的区分,提高数据的可读性。然后,将这个图层添加到MapContent
中,最后使用GeoTools的功能,将这些内容渲染成一张清晰展示雷达数据的PNG图片。
/*** 根据坐标和值创建地图图像。** @param coordinates 坐标对象列表,每个对象包含坐标点和与之相关联的值。* @param outputPath 图像输出路径,指定生成的地图图像保存的位置。* @throws Exception 抛出异常,处理文件操作或渲染过程中可能出现的错误。*/public static void createMapImageVal(ArrayList<CoordinatePojo> coordinates, String outputPath) throws Exception {// 步骤1: 定义SimpleFeatureTypeSimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();typeBuilder.setName("Point");typeBuilder.setCRS(DefaultGeographicCRS.WGS84); // 坐标参考系统typeBuilder.add("location", Point.class); // 添加位置字段typeBuilder.add("value", Double.class); // 添加值字段SimpleFeatureType TYPE = typeBuilder.buildFeatureType(); // 构建特征类型// 步骤2: 创建SimpleFeatureCollectionDefaultFeatureCollection featureCollection = new DefaultFeatureCollection("internal", TYPE);GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);// 为每个坐标点创建特征并添加到集合中for (CoordinatePojo data : coordinates) {Point point = geometryFactory.createPoint(data.getCoordinate()); // 根据坐标创建点// 根据点的值创建SimpleFeatureSimpleFeature feature = SimpleFeatureBuilder.build(TYPE, new Object[]{point, data.getValue()}, null);featureCollection.add(feature); // 将特征添加到集合}// 创建基于值的点样式Style style = createValueBasedPointStyle(); // 假设这是一个自定义方法创建基于值的样式// 步骤3: 定义样式// 这里我们使用了自定义的创建样式方法,而不是SLD.createPointStyle// 步骤4: 渲染图像MapContent mapContent = new MapContent();mapContent.setTitle("Sample Map"); // 设置地图标题Layer layer = new FeatureLayer(featureCollection, style); // 创建图层mapContent.addLayer(layer); // 将图层添加到地图内容GTRenderer renderer = new StreamingRenderer();renderer.setMapContent(mapContent); // 设置地图内容Rectangle imageBounds = null;ReferencedEnvelope mapBounds = null;try {mapBounds = mapContent.getMaxBounds(); // 获取最大边界double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0); // 计算高宽比imageBounds = new Rectangle(0, 0, 800, (int) Math.round(800 * heightToWidth)); // 设置图像边界} catch (Exception e) {// 处理可能的异常}BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_ARGB);Graphics2D gr = image.createGraphics();gr.setPaint(Color.WHITE);gr.fill(imageBounds); // 填充背景色renderer.paint(gr, imageBounds, mapBounds); // 渲染图像File file = new File(outputPath); // 创建文件ImageIO.write(image, "png", file); // 写入图像文件gr.dispose(); // 释放资源mapContent.dispose(); // 释放地图内容}
完整代码
GenerateMapImage地图渲染工具
import com.zykj.radar_server.datahandle.RadarDataParser;
import com.zykj.radar_server.datahandle.pojo.LonLatData;
import com.zykj.radar_server.entity.pojo.CoordinatePojo;
import com.zykj.radar_server.entity.pojo.RadarDataPojo;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.renderer.GTRenderer;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.SLD;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;import org.geotools.styling.*;
import org.opengis.filter.FilterFactory2;public class GenerateMapImage {/*** 根据坐标和值创建地图图像。** @param coordinates 坐标对象列表,每个对象包含坐标点和与之相关联的值。* @param outputPath 图像输出路径,指定生成的地图图像保存的位置。* @throws Exception 抛出异常,处理文件操作或渲染过程中可能出现的错误。*/public static void createMapImageVal(ArrayList<CoordinatePojo> coordinates, String outputPath) throws Exception {// 步骤1: 定义SimpleFeatureTypeSimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();typeBuilder.setName("Point");typeBuilder.setCRS(DefaultGeographicCRS.WGS84); // 坐标参考系统typeBuilder.add("location", Point.class); // 添加位置字段typeBuilder.add("value", Double.class); // 添加值字段SimpleFeatureType TYPE = typeBuilder.buildFeatureType(); // 构建特征类型// 步骤2: 创建SimpleFeatureCollectionDefaultFeatureCollection featureCollection = new DefaultFeatureCollection("internal", TYPE);GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);// 为每个坐标点创建特征并添加到集合中for (CoordinatePojo data : coordinates) {Point point = geometryFactory.createPoint(data.getCoordinate()); // 根据坐标创建点// 根据点的值创建SimpleFeatureSimpleFeature feature = SimpleFeatureBuilder.build(TYPE, new Object[]{point, data.getValue()}, null);featureCollection.add(feature); // 将特征添加到集合}// 创建基于值的点样式Style style = createValueBasedPointStyle(); // 假设这是一个自定义方法创建基于值的样式// 步骤3: 定义样式// 这里我们使用了自定义的创建样式方法,而不是SLD.createPointStyle// 步骤4: 渲染图像MapContent mapContent = new MapContent();mapContent.setTitle("Sample Map"); // 设置地图标题Layer layer = new FeatureLayer(featureCollection, style); // 创建图层mapContent.addLayer(layer); // 将图层添加到地图内容GTRenderer renderer = new StreamingRenderer();renderer.setMapContent(mapContent); // 设置地图内容Rectangle imageBounds = null;ReferencedEnvelope mapBounds = null;try {mapBounds = mapContent.getMaxBounds(); // 获取最大边界double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0); // 计算高宽比imageBounds = new Rectangle(0, 0, 800, (int) Math.round(800 * heightToWidth)); // 设置图像边界} catch (Exception e) {// 处理可能的异常}BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_ARGB);Graphics2D gr = image.createGraphics();gr.setPaint(Color.WHITE);gr.fill(imageBounds); // 填充背景色renderer.paint(gr, imageBounds, mapBounds); // 渲染图像File file = new File(outputPath); // 创建文件ImageIO.write(image, "png", file); // 写入图像文件gr.dispose(); // 释放资源mapContent.dispose(); // 释放地图内容}// StyleFactory 用于创建地图样式元素,例如图层的边框、填充等。private static final StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();// FilterFactory2 用于创建过滤条件,例如,在样式规则中选择符合特定条件的要素。private static final FilterFactory2 filterFactory = CommonFactoryFinder.getFilterFactory2();/*** 根据数值创建基于值的点样式。** @return 样式对象,根据不同的值范围应用不同的颜色。*/public static Style createValueBasedPointStyle() {// 创建一个黑色描边,宽度为1。Stroke stroke = styleFactory.createStroke(filterFactory.literal(Color.BLACK), filterFactory.literal(1));FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle();// 定义值的范围及其对应的颜色(十六进制表示)。Object[][] valueRanges = {{-5, Double.NEGATIVE_INFINITY, "#00acb8"},{0, -5, "#c1c0ac"},{10, 0, "#7a71fe"},{15, 10, "#1e26e3"},{20, 15, "#a6fcbb"},{25, 20, "#00ea15"},{30, 25, "#10932d"},{35, 30, "#fcf477"},{40, 35, "#c9c815"},{45, 40, "#8c8c12"},{50, 45, "#feadc0"},{55, 50, "#ff6370"},{60, 55, "#ee0244"},{65, 60, "#d48eff"},{Double.POSITIVE_INFINITY, 65, "#ab23ff"}};// 遍历每个值范围,为每个范围创建一个规则并添加到FeatureTypeStyle。for (Object[] range : valueRanges) {fts.rules().add(createRuleForRange(Double.parseDouble(range[1].toString()), Double.parseDouble(range[0].toString()), range[2].toString()));}// 创建样式并添加FeatureTypeStyle。Style style = styleFactory.createStyle();style.featureTypeStyles().add(fts);return style;}/*** 根据给定的值范围和颜色创建一个规则。** @param min 范围的最小值。* @param max 范围的最大值。* @param hexColor 颜色的十六进制表示。* @return 样式规则,包含一个基于值范围的符号化表示。*/public static Rule createRuleForRange(double min, Object max, String hexColor) {// 根据十六进制颜色创建填充,并设置不透明度为1.0。Fill fill = styleFactory.createFill(filterFactory.literal(hexToColor(hexColor)), filterFactory.literal(1.0));// 获取默认的圆形标记,并设置填充和描边。Mark mark = styleFactory.getCircleMark();mark.setFill(fill);mark.setStroke(null); // 不使用描边。// 创建图形对象,清除默认的图形符号,添加自定义的标记。Graphic graphic = styleFactory.createDefaultGraphic();graphic.graphicalSymbols().clear();graphic.graphicalSymbols().add(mark);graphic.setSize(filterFactory.literal(4)); // 设置图形大小。// 创建点符号器,应用上面的图形。PointSymbolizer symbolizer = styleFactory.createPointSymbolizer(graphic, null);// 创建规则,设置过滤条件为值在指定范围内。Rule rule = styleFactory.createRule();rule.symbolizers().add(symbolizer);rule.setFilter(filterFactory.between(filterFactory.property("value"),filterFactory.literal(min),filterFactory.literal(max)));return rule;}/*** 将十六进制颜色字符串转换为Color对象。** @param hex 十六进制颜色字符串(例如,"#ff0000"表示红色)。* @return 转换后的Color对象。*/public static Color hexToColor(String hex) {// 将十六进制字符串转换为整数,并创建颜色对象。// 这里没有添加前缀"FF",因为Color类的构造函数默认处理不透明颜色。return new Color(Integer.parseInt(hex.substring(1), 16));}}
测试代码
测试数据自行准备,如何解析雷达数据请看我上一篇博客。
package com.zykj.radar_server.data;import com.zykj.radar_server.datahandle.GenerateMapImage;
import com.zykj.radar_server.datahandle.RadarDataParser;
import com.zykj.radar_server.datahandle.pojo.LonLatData;
import com.zykj.radar_server.entity.pojo.CoordinatePojo;
import com.zykj.radar_server.entity.pojo.RadarDataPojo;import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.Coordinate;
import org.springframework.boot.test.context.SpringBootTest;import java.util.ArrayList;
import java.util.List;@SpringBootTest
public class GenerateMapImageTest {@Testpublic void test() throws Exception {String filePath = "D:\\雷达测试数据\\数据\\20190305_110801_ZHBJ_Z_VOL_2.50.dat";RadarDataPojo radarDataPojo = RadarDataParser.handlePolarProductForPojo(filePath);ArrayList<LonLatData> lonLatDataList = radarDataPojo.getLonLatDataList();System.out.println(lonLatDataList.size());ArrayList<CoordinatePojo> coordinates = new ArrayList<>();for (int i = 0; i < lonLatDataList.size(); i++) {CoordinatePojo coordinatePojo = new CoordinatePojo();coordinatePojo.setCoordinate(new Coordinate(lonLatDataList.get(i).getLon(), lonLatDataList.get(i).getLat()));double val = lonLatDataList.get(i).getVal();coordinatePojo.setValue(val);coordinates.add(coordinatePojo);}// 指定输出图片路径String outputPath = "D:\\temp\\layers\\jiangsu_map_val4.png";GenerateMapImage.createMapImageVal(coordinates, outputPath);System.out.println("地图图片已生成: " + outputPath);}}
渲染效果
雷达数据渲染效果如下所示,颜色越深回波强度越大:
渲染点的大小可自行调节。
相关文章:

Java项目实战记录:雷达数据渲染
目录 Java项目实战记录:雷达数据渲染业务背景代码逻辑数据结构颜色渲染MapContent加载数据并输出截图 完整代码GenerateMapImage地图渲染工具测试代码 渲染效果 Java项目实战记录:雷达数据渲染 业务背景 我之前已经成功使用Java语言解析了C处理的雷达数…...

进程的概念 | PCB | Linux下的task_struct | 父子进程和子进程
在讲进程之前首先就是需要去回顾一下我们之前学的操作系统是干嘛的,首先操作系统是一个软件,它是对上提供一个良好高效,稳定的环境的,这是相对于用户来说的,对下是为了进行更好的软硬件管理的,所以操作系统…...

【GPT-SOVITS-03】SOVITS 模块-生成模型解析
说明:该系列文章从本人知乎账号迁入,主要原因是知乎图片附件过于模糊。 知乎专栏地址: 语音生成专栏 系列文章地址: 【GPT-SOVITS-01】源码梳理 【GPT-SOVITS-02】GPT模块解析 【GPT-SOVITS-03】SOVITS 模块-生成模型解析 【G…...

2024HVV行动-进军蓝中研判(log4j2、fastjson、Struts2、Shiro)
1、log4j2 特征: 恶意请求中包含 JNDI 协议地址,如"ldap://"、"rmi://"等,被 log4j2 解析为 JNDI 查找。 原理: 在日志输出中,未对字符进行严格的过滤,执行了 JNDI 协议加载的远程恶…...

亮点抢先看!4月16-17日,百度Create大会开设“AI公开课”,大咖带你打造赚钱工具
3月16日,2024百度Create AI开发者大会正式开放售票,嘉宾套票定价399元。据悉,本次大会以“创造未来(Create the Future)”为主题,设有20深度论坛、超30节AI公开课、3000平AI互动体验区和AI音乐节等精彩环节…...

【笔记本清灰/实用经验】荣耀Magicbook14-2020款-R5-4500U-清灰实战
清灰有风险,动手需谨慎,本文只分享本人的清灰过程,对使用它所产生的任何后果不任何负责任 文章目录 背景信息准备阶段工具准备信息收集 正式清灰初始化清灰流程放掉身体的静电(重要)拆笔记本后盖断开电源(重…...

如何写好Stable Diffusion的prompt
Stable Diffusion是一种强大的文本到图像生成模型,其效果在很大程度上取决于输入的提示词(Prompt)。以下是一些关于如何编写有效的Stable Diffusion Prompt的秘诀: 明确描述:尽量清晰地描述你想要的图像内容。使用具体…...

计算机毕业设计 | SpringBoot+vue 移动端社区物业管理系统(附源码+论文)
1, 概述 课题背景 近几年来,随着物业相关的各种信息越来越多,比如报修维修、缴费、车位、访客等信息,对物业管理方面的需求越来越高,我们在工作中越来越多方面需要利用网页端管理系统来进行管理,我们所需…...

玩转C语言——数组初探
一、前言 通过前面的学习,我们已了解C语言的结构变量、分支结构和循环结构。今天,我们一起来认识C语言的另一知识点——数组。先赞后看,养成习惯。 二、数组概念 学习数组,我们要明白数组是什么。在我看来:数组是⼀组…...
Nginx指令配置大全
基本命令 nginx -t 检查配置文件是否有语法错误 nginx -s reload 热加载,重新加载配置文件 nginx -s stop 快速关闭 nginx -s quit 等待工作进程处理完成后关闭配置块介绍 全局块 全局块是默认配置文件从开始到events块之间的…...
富格林:安全出金关注可信操作
富格林悉知,现货黄金投资凭借着诸多优势,成为了热门的投资产品之一,也获得了投资者的追捧。在投资中想要安全盈利出金,投资者一定要沉下心来学习专业知识和技术,这样才能在以后的投资操作中避免亏损,顺畅盈…...
DELETE、TRUNCATE 和 DROP 在MySQL中的区别及使用示例
在MySQL数据库中,DELETE、TRUNCATE TABLE 和 DROP 这三个命令分别适用于不同的数据删除需求,它们在工作原理、应用场景以及特性上有所区别。接下来,我们通过实例演示来明确这三者的不同之处。 DELETE 命令 功能与示例:DELETE 语…...
程序员应该如何选择职业赛道?
程序员选择职业赛道是一个涉及个人兴趣、技能匹配、市场需求和长远发展规划的综合决策过程。以下是一些关键步骤和考虑因素: 自我评估: 技能与专长:分析自己在编程语言、算法、数据结构等方面的现有技能,并思考这些技能更适合前端…...

深入浅出Hive性能优化策略
我们将从基础的HiveQL优化讲起,涵盖数据存储格式选择、数据模型设计、查询执行计划优化等多个方面。会的直接滑到最后看代码和语法。 目录 引言 Hive架构概览 示例1:创建表并加载数据 示例2:优化查询 Hive查询优化 1. 选择适当的文件格…...
利用卷积神经网络进行人脸识别
利用卷积神经网络(Convolutional Neural Networks, CNNs)进行人脸识别是计算机视觉领域的一个热门话题。下面是一个简化的指南,涵盖了从理论基础到实际应用的各个方面,可以作为你博文的基础内容。 理论基础 卷积神经网络简介&am…...

固态硬盘有坏道怎么恢复数据 固态硬盘坏道怎么修复
固态硬盘是一种高速、低噪音、低功耗的存储设备,但是它也有一个致命的问题——坏道。坏道是指存储芯片中的某些存储单元出现了故障,导致数据无法正常读取或写入。如果你的固态硬盘出现了坏道,那么你的数据就有可能会丢失,带来了很大的困扰。那么,固态硬盘有坏道怎么恢复数…...

adobe animate 时间轴找不到编辑多个帧按钮
如题,找了半天,在时间轴上找不到编辑多个帧按钮,导致无法批量处理帧 然后搜索发现原来是有些版本被隐藏了,需要再设置一下 勾选上就好了...

5 亿欧元巨额奖励!法国国防部启动量子初创公司项目
内容来源:量子前哨(ID:Qforepost) 编辑丨王珩 编译/排版丨沛贤 深度好文:800字丨6分钟阅读 据C4ISNET报道,法国国防部采购机构宣布向五家法国量子计算研究初创公司授予合同,用于开发量子计算技…...

Linux:系统初始化,内核优化,性能优化(2)
优化ssh协议 Linux:ssh配置_ssh配置文件-CSDN博客https://blog.csdn.net/w14768855/article/details/131520745?ops_request_misc%257B%2522request%255Fid%2522%253A%2522171068202516800197044705%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fb…...
JS08-DOM节点
DOM节点 查找节点 父节点 通过.parentNode属性可以获得某个元素的父节点,并对其进行操作。例如,隐藏.son元素的父节点。 <div class"father"><div class"son">儿子</div></div><script>let son d…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...