JAVA代码 实现定位数据动态聚集并绘制多边形区域
文章目录
- 思路
- 1、限制聚合距离
- 2、绘制多边形区域
- 3、多边形区域之间合并
- 4、多边形定边点
- 4、逻辑流程
- 一些性能上的优化
- 1、多边形设置圆心
- 2、采用分支合并思路
- 3、清理聚集较分散区域
- 合理性处理
- 1、解决多边形内凹角问题
- 2、解决定边点插入位置问题
- 3、多边形区域扩展
- 成果展示
最近有根据一堆离散的报警数据(内部包含经纬度 报警类型)需要聚合出每个报警发生的区域需求,比如 超速报警 聚集点有哪些,疲劳驾驶报警有哪些等等,个人没有采用已成型的算法 比如DBSCAN,而是自己实现

思路
为了让聚合的区域更精确,以及聚合的区域过大,我们采取了如下措施
1、限制聚合距离
何为限制聚合距离呢,就是定位点与定位点间 或定位点与多边形区域之间设置距离限制,如果点与点 或点与多边形距离小于设置阈值,则点加入该区域,否则成都一个独立区域?
2、绘制多边形区域
为了避免区域内非聚集空白区过多,我们取消了圆形区域,决定基于聚集的点形成多边形区域
3、多边形区域之间合并
为什么多边形区域之间需要合并呢,随着区域的增多,可能一个点同时与多个多边形区域距离符合阈值需要加入,那此时,对应的多边形就需要合并在一起组成新的区域
4、多边形定边点
组成多边形我们就使用定位数据点,并按顺序存入,最后前端根据定位点顺序绘制出多边形(A>B>C) 那么前端就会基于 A>B B>C C>A 绘制出一个三角形区域
报警模型
/*** @author lei* @create 2023-02-27 15:51* @desc**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Alarm {private Integer vehicleId;@ApiModelProperty("经度")private Integer longitude;@ApiModelProperty("纬度")private Integer latitude;private String pointName;private Integer alarmType;public Alarm(Integer vehicleId, Integer longitude, Integer latitude, String pointName) {this.vehicleId = vehicleId;this.longitude = longitude;this.latitude = latitude;this.pointName = pointName;}
}
4、逻辑流程
基于当前定位点循环当前所有的多边形区域,与区域元素进行判断,满足则加入(contine区域列表并记录已有区域)并与剩下区域列表中区域依次比对,不满足则新建区域,已有满足区域但还有匹配区域则两个区域进行合并
private static boolean collectCalc(List<PolygonMapArea> polygonMapAreas, Alarm curAlarm, Double collectDistance) {// 是否需要初始化新多边形boolean needInitNewPolygon = true;PolygonMapArea belongedArea = null;MapPoint curPoint = new MapPoint(curAlarm.getLongitude(), curAlarm.getLatitude(), curAlarm.getPointName());for (PolygonMapArea polygonMapArea : polygonMapAreas) {if (!polygonMapArea.isLive()) {continue;}// 先算圆心到当前点距离是否超过指定阈值,超过则进行下一个多边形判断double distance = AreaUtil.distance(polygonMapArea.getCycleCenter(), curPoint);if (distance > polygonMapArea.getRadius() + collectDistance) {continue;}if (polygonMapArea.getPoints().getSize() == 1) {needInitNewPolygon = false;if (belongedArea == null) {// 归属当前聚集点,计算最大经纬度与圆心polygonMapArea.getAlarms().add(curAlarm);polygonDrawCircle(curPoint, polygonMapArea, distance);polygonMapArea.setBelong(true);belongedArea = polygonMapArea;} else {mergePolygonArea(belongedArea, polygonMapArea, collectDistance);}continue;}// 属于当前多边形扫描圆距离,则进行多边形内外部判断boolean inPolygonArea = AreaUtil.locateInPolygonArea(curPoint, polygonMapArea);if (inPolygonArea) {// 当前点在现多边形内needInitNewPolygon = false;if (belongedArea == null) {polygonMapArea.getAlarms().add(curAlarm);polygonMapArea.setBelong(true);belongedArea = polygonMapArea;} else {mergePolygonArea(belongedArea, polygonMapArea, collectDistance);}continue;}// 当前点在多边形之外,则与各边进行判断,找到与其相邻最近边,如距离小于指定阈值则加入多边形Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> nearestEdge = getNearestEdge(curPoint, polygonMapArea.getPoints());if (nearestEdge == null) {log.warn("当前点:{}未找到最近边", curAlarm);continue;}Pair<Node<MapPoint>, Node<MapPoint>> nearestNode = nearestEdge.getKey();double nearestEdgeDistance = nearestEdge.getValue();if (nearestEdgeDistance <= collectDistance) {needInitNewPolygon = false;if (belongedArea == null) {polygonMapArea.getAlarms().add(curAlarm);if (nearestEdgeDistance > AreaUtil.ON_LINE_DISTANCE) {// 加入多边形并削内凹角tryRecalculationArea(polygonMapArea, nearestNode.getKey(), nearestNode.getValue(), curPoint);}polygonMapArea.setBelong(true);belongedArea = polygonMapArea;} else {mergePolygonArea(belongedArea, polygonMapArea, collectDistance);}}}return needInitNewPolygon;}
一些性能上的优化
1、多边形设置圆心
多边形圆心是由 多边形定边点数据中洗出来的 (最大经度,最大纬度) (最小经度最小纬度) 获取出的中心点
当判断点是否加入多边形时 先与圆心距离判断 (点与圆心点距离小于等于 圆半径+聚合距离是着可能需要加入,如大于着必不可能加入该多边形)
2、采用分支合并思路
区域列表循环采用分支合并思路,基于当前机器核心线程数拆分现有区域列表,异步计算最后合并汇总数据
/*** 获取对应报警类型计算线程池** @param alarm* @return ThreadPoolExecutor* @author lei* @date 2023-03-27 11:06:57*/private static ThreadPoolExecutor getCalcExecutor(Alarm alarm) {return ALARM_TYPE_EXECUTOR_MAP.computeIfAbsent(alarm.getAlarmType(), alarmType -> {alarmType = Optional.ofNullable(alarmType).orElse(0);ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE, CORE, 10, TimeUnit.MINUTES,new ArrayBlockingQueue<>(1024), new ThreadFactoryBuilder().setNameFormat(alarmType + "-calc-%d").setUncaughtExceptionHandler((t, e) -> log.error("线程:{}处理异常", t.getName(), e)).build(), new ThreadPoolExecutor.CallerRunsPolicy());log.debug("初始化报警类型:{}计算线程池!", alarmType);return executor;});}/*** 加入多边形计算** @param polygonMapAreas 现有聚集区* @param curAlarm 当前报警* @param collectDistance 聚集距离* @param calcIncr 累计计算量* @param incrId* @return void* @author lei* @date 2023-03-08 11:24:30*/private static void forkJoinPolygonArea(List<PolygonMapArea> polygonMapAreas, Alarm curAlarm, Double collectDistance,AtomicLong calcIncr, AtomicLong incrId) {long calcNum = calcIncr.incrementAndGet();if (CollUtil.isEmpty(polygonMapAreas)) {initPolygonArea(curAlarm, polygonMapAreas, incrId);return;}boolean needInitNewPolygon;List<List<PolygonMapArea>> splitAreaList = splitPolygonArea(polygonMapAreas);int splitAreaSize = splitAreaList.size();if (splitAreaSize > 1) {// 与各个子分支区域列表计算ThreadPoolExecutor calcExecutor = getCalcExecutor(curAlarm);List<CompletableFuture<Boolean>> collect = splitAreaList.stream().map(areas ->CompletableFuture.supplyAsync(() -> collectCalc(areas, curAlarm, collectDistance), calcExecutor)).collect(toList());List<Boolean> forkResultList = collect.stream().map(CompletableFuture::join).collect(toList());// 分支结果全真为真,非全真则再次合并needInitNewPolygon = splitAreaSize == forkResultList.stream().filter(x -> x).count();if (!needInitNewPolygon) {forkMergePolygonArea(polygonMapAreas, collectDistance);}} else {needInitNewPolygon = collectCalc(polygonMapAreas, curAlarm, collectDistance);}if (needInitNewPolygon) {initPolygonArea(curAlarm, polygonMapAreas, incrId);}// 清理较为离散的区域cleanDispersedArea(polygonMapAreas, calcNum);}/*** 合并线程子分支区域计算结果** @return List<PolygonMapArea> 返回分支合并后的区域列表,此时的区域列表为当前定位点计算后的最新结果* @author lei* @date 2023-03-27 15:46:26*/private static void forkMergePolygonArea(List<PolygonMapArea> polygonMapAreas, Double collectDistance) {PolygonMapArea belongArea = null;for (PolygonMapArea polygonMapArea : polygonMapAreas) {if (polygonMapArea.isBelong() && polygonMapArea.isLive()) {if (belongArea == null) {belongArea = polygonMapArea;} else {mergePolygonArea(belongArea, polygonMapArea, collectDistance);}}}}
3、清理聚集较分散区域
我们采取了定量清理区域逻辑思路,比如计算了20000个定位点清理一部分区域(区域一个点),区域数量达到10000个时清理区域内小于三个点的数据
合理性处理
1、解决多边形内凹角问题
由于每个定位点都有可能成为组成多边形区域的点,故此有可能组成的多边形是弯弯曲曲的畸形,比如像手,像魔爪等等,不是一个外凸的多边形
我们期望减少内凹角,使其成为一个更加饱满的多边形区域,比如下图
使用jts依赖库
compile 'org.locationtech.jts:jts-core:1.19.0'
核心思路是拿需消除内凹角的多边形所有定边点,使用jts依赖库绘制图形然后获取凸包,然后过滤出多边形定边点 不在凸包列表的数据
// 转换多边形定边点数据
Coordinate[] coordinates = points.getList().stream().map(x -> {Coordinate coordinate = new Coordinate(x.getLongitude(), x.getLatitude());return coordinate;}).toArray(Coordinate[]::new);
// 计算凸包
ConvexHull convexHull = new ConvexHull(coordinates, new GeometryFactory());
Geometry geometry = convexHull.getConvexHull();
Set<Coordinate> after = Arrays.stream(geometry.getCoordinates()).collect(Collectors.toSet());
// 踢出多边形定边点中不在Set集合中的数据
2、解决定边点插入位置问题
当新的定位点时要成为组成多边形定边点的时候会面临一个问题,这个点要插入哪里呢(插入顺序)?因为一旦位置选错了后,绘制成的多边形地图就会交叉
比如下图的多边形

先分析上图问题产生原因
1、最开始 是 2 3 4组成三角区域,5点到来时,需要先判断该点与多边形哪一边更近(答案很显然,与 3 4 更近)于是加入了 3 后 成为新多边形 2 3 5 4
6点到来,则需要与多边形 2 3 5 4判断最近边,但此时其与 3 5 54 边距离是相等的,由于我逻辑的不完整性,选错了边,选了3 5 边,绘制最终多边形 2 3 6 5 4,导致多边形交叉
如何解决选错边的问题?如何选择插入的边?
思考了很久,我们可以将要插入的点与最近两个边夹角大小来判断,即上图的 3 5 6 与 4 5 6两个夹角的大小来判断,插入夹角较小的那一边。
35 54 一个最近边的结束 也是另一个最近边的开始
但还特别需要注意的是 多边形的组成顺序并不一定都是我上图的 2 3 5…这样顺时针(多边形从右边开始绘制)组成的,也可能多边形是逆时针绘制组成出来的(多边形从左边开始生成绘制)
抛除6点,我们假设上图多边形是从左边绘制的,并且 6点不是与 35 54最近,而是与 24 32 最近,那么此时会发现,相邻的两个边,一个最近边的结束不是另一个最近边的开始 这个时候我们夹角度数就要修改为 4 2 6 与 3 2 6,这样计算夹角才对
那么我们如何确定顺时针与逆时针呢?我们只需要判断第二个最近边的开始是不是第一边的结束即可
/*** 根据当前点 获取与当前多边形最近边** @param curPoint 当前点* @param points 当前多边形组合点* @return Pair<Pair < Node < MapPoint>,Node<MapPoint>>,Double> 最近边组成点 当前点与最近边距离* @author lei* @date 2023-03-02 15:45:18*/private static Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> getNearestEdge(MapPoint curPoint, LinkList<MapPoint> points) {Node<MapPoint> foreachNode = points.getFirst();Double nearestEdgeDistance = null;List<Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double>> pairList = new ArrayList<>();for (int i = 0; i < points.getSize(); i++) {Node<MapPoint> endPoint = foreachNode.getNext();double curDistance = AreaUtil.pointToLine(foreachNode.getData(), endPoint.getData(), curPoint);if (nearestEdgeDistance == null || nearestEdgeDistance >= curDistance) {nearestEdgeDistance = curDistance;Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> nearestEdge = new Pair<>(Pair.of(foreachNode, foreachNode.getNext()), curDistance);pairList.add(nearestEdge);}foreachNode = endPoint;}Map<Double, List<Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double>>> map = pairList.stream().collect(Collectors.groupingBy(Pair::getValue));List<Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double>> nearestEdgeList = map.get(nearestEdgeDistance);// 特殊情况会出现当前点d与多边形两个边 (顺时针查找ab bc、或逆时针查找 ab ca)距离相同情况,因此做特殊处理,返回其夹角度数最小所在的那一边if (points.getSize() >= 3 && nearestEdgeList.size() == 2) {// 边1Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> nearestEdgeOne = nearestEdgeList.get(0);Pair<Node<MapPoint>, Node<MapPoint>> edgeOne = nearestEdgeOne.getKey();Node<MapPoint> edgeOneStart = edgeOne.getKey();Node<MapPoint> edgeOneEnd = edgeOne.getValue();// 边2Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> nearestEdgeTwo = nearestEdgeList.get(1);Pair<Node<MapPoint>, Node<MapPoint>> edgeTwo = nearestEdgeTwo.getKey();Node<MapPoint> edgeTwoStart = edgeTwo.getKey();Node<MapPoint> edgeTwoEnd = edgeTwo.getValue();double cosValue1;double cosValue2;// 顺时针相邻if (edgeTwoStart.equals(edgeOneEnd)) {// 求 边1 开始 结束 当前点组成夹角余弦 (a b d)cosValue1 = AreaUtil.cosValue(edgeOneStart.getData(), edgeOneEnd.getData(), curPoint);// 求 边2 结束 开始 当前点组成夹角余弦 (c b d)cosValue2 = AreaUtil.cosValue(edgeTwoEnd.getData(), edgeTwoStart.getData(), curPoint);} else {// a b c a// 求 边1 结束 开始 当前点组成夹角余弦 (b a d)cosValue1 = AreaUtil.cosValue(edgeOneEnd.getData(), edgeOneStart.getData(), curPoint);// 求 边2 开始 结束 当前点组成夹角余弦 (c a d)cosValue2 = AreaUtil.cosValue(edgeTwoStart.getData(), edgeTwoEnd.getData(), curPoint);}// 返回小夹角所在边,如夹角一致则交由下一步消内凹逻辑return cosValue1 >= cosValue2 ? nearestEdgeOne : nearestEdgeTwo;}return nearestEdgeList.get(0);}
3、多边形区域扩展
我们生成了某一类型报警的 N个聚集区域后,当其他车辆快要经过这里时我们怎么提前预警呢?
1、基于现在车辆定位点计算其与报警聚集区距离,小于等于阈值则报警
此方案可行,但是如果车辆速度过快(且我们的车辆定位是30s传一条)且聚集区比较小的话可能还没起到提示作用就已经过了该聚集区了
2、在原有多边形区域基础之上,向外扩展指定阈值,当车辆在记录扩展后的区域达到阈值或在进入到扩展后的区域时触发提醒
我们最终选择了方案二
那么多边形区域如何扩展呢?
使用jts依赖库
compile 'org.locationtech.jts:jts-core:1.19.0'
我们将原多边形区域定边点使用jts依赖库组成几何图形,并设置外扩,我们拿到外扩后的几何图形凸包即可
/*** 多边形区域扩展** @param points 定位点* @param extendDistance 外扩距离 m* @return List<MapPoint>* @author lei* @date 2023-03-21 16:19:30*/
public static List<MapPoint> extendPoints(List<MapPoint> points, double extendDistance) {List<MapPoint> extendsPoints = points;if (extendDistance > 1 && points.size() >= NEED_EXTEND_SIZE) {Coordinate[] coordinates = points.stream().map(point -> new Coordinate(point.getLongitude(), point.getLatitude())).toArray(Coordinate[]::new);ConvexHull convexHull = new ConvexHull(coordinates, new GeometryFactory());Geometry polygon = convexHull.getConvexHull();// 每个圆弧的线段数,值越夹角越平滑,int quadrantSegments = 1;// 端点类型int endCapStyle = BufferParameters.CAP_SQUARE;// 连接类型int joinStyle = BufferParameters.JOIN_MITRE;// 指定斜接连接的斜率限制 当两个线段夹角很小时,斜接点锐角非常尖锐; 避免锐化可限制斜接点的斜率,当斜率超过指定阈值将强使用JOIN_BEVELdouble mitreLimit = 5.0;BufferParameters bufferParams = new BufferParameters(quadrantSegments, endCapStyle, joinStyle, mitreLimit);bufferParams.setSingleSided(true);// 向外扩展区域Geometry expandedPolygon = BufferOp.bufferOp(polygon, extendDistance * 10, bufferParams);Coordinate[] extendArray = expandedPolygon.convexHull().getCoordinates();extendsPoints = Arrays.stream(extendArray).map(x -> new MapPoint((int) x.x, (int) x.y)).distinct().collect(Collectors.toList());}return extendsPoints;
}
成果展示
相关文章:

JAVA代码 实现定位数据动态聚集并绘制多边形区域
文章目录思路1、限制聚合距离2、绘制多边形区域3、多边形区域之间合并4、多边形定边点4、逻辑流程一些性能上的优化1、多边形设置圆心2、采用分支合并思路3、清理聚集较分散区域合理性处理1、解决多边形内凹角问题2、解决定边点插入位置问题3、多边形区域扩展成果展示最近有根据…...

基于储能进行调峰和频率调节研究【超线性增益的联合优化】(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

体验 Linux 的几个监控命令(htop、nmon、netdata)
体验 Linux 的几个监控命令htopnmonnetdatahtop 安装, sudo dnf install -y htop使用, htopnmon 安装, sudo dnf install -y nmon使用, nmon输入c, 输入C, 输入m, 输入n, 输入…...

NOC大赛2022NOC软件创意编程初赛图形化小低组(小学高年级组)
一、选择题 1.如果要控制所有角色一起朝舞台区右侧移动,下面哪个积太块是不需要的 2.要想让三个角色一起移动起来,下面哪个积木块没有作用 ? 3.小猴按照下面的程序前进,小猴最后一次前进了()步。 4.小可同学写了一个画笔程序画出花朵,但是运行后什么都看不到,不可…...
python进行股票收益率计算和风险控制的实现
股票收益率计算和风险控制的实现 在进行股票投资时,计算收益率和进行风险控制是非常重要的。本文将介绍一个与此相关的函数:radio_day_cal()。 radio_day_cal()函数 def radio_day_cal(last_day, sheet_name, df_dict, code_list, new_list):i 0days…...

自从有了这套近4000页的开发文档后,Java面试路上就像开了挂一样
Java是世界最流行的编程语言,也是国内大多数IT公司的主流语言。招聘网站上Java岗位众多,Java工程师似乎不愁找工作。但仔细一看就会发现,Java岗位的招聘薪酬天差地别,人才要求也是五花八门。而在Java工程师求职过程中,…...

Python文件操作
目录 一、文件操作介绍 二、文件的打开和关闭 三、文件的读写 四、文件文件夹相关操作 五、test 一、文件操作介绍 文件 : python中文件是对象 Liunx 文件 : 一切设备都可以看成是文件 磁盘文件 管道 网络Socket 文件属性: 读 写 执行权限 就是把一些存储存放起来&…...

036:cesium加载GPX文件,显示图形
第036个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载GPX文件, 显示图形。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共83行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:https:/…...

【AI探索】我问了ChatGPT几个终极问题
终于尝试了一把ChatGPT的强大之处,问了一下关心的几个问题: chatGPT现在在思考吗?有没有什么你感兴趣的问题? 你认为AI会对人类产生哪些方面的影响? 你对人类所涉及到的学科有了解吗?你认为在哪些方面与人类…...

Leetcode 优先队列详解
优先队列 优先队列(Priority Queue):一种特殊的队列。在优先队列中,元素被赋予优先级,当访问队列元素时,具有最高优先级的元素最先删除 普通队列详解Leetcode 队列详解 优先队列与普通队列最大的不同点在于…...

通过两道一年级数学题反思自己
背景 做完这两道题我开始反思自己,到底是什么限制了我?是我自己?是曾经教导我的老师?还是我的父母? 是考试吗?还是什么? 提目 1、正方体个数问题 2、相碰可能性 过程 静态思维: …...

Pytorch :从零搭建一个神经网络
文章目录安装依赖从源码编译pytorchCXX_ABI问题数据集归一化Transforms搭建神经网络Components of a neural networknn.Flattennn.Linearnn.Sequentialnn.SoftmaxModel Parameters优化模型参数设置超参数添加优化循环添加 loss function优化过程完整实现模型的保存和加载安装 …...
【华为OD机试 2023最新 】 区块链文件转储系统(C++ 100%)
题目描述 区块链底层存储是一个链式文件系统,由顺序的N个文件组成,每个文件的大小不一,依次为F1,F2,…,Fn。随着时间的推移,所占存储会越来越大。 云平台考虑将区块链按文件转储到廉价的SATA盘,只有连续的区块链文件才能转储到SATA盘上,且转储的文件之和不能超过SATA盘…...

基于springcloud实现分布式架构网上商城演示【项目源码】分享
基于springcloud实现分布式架构网上商城演示摘要 首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了系统的需求基础上需要进一步地设计系统,主要包…...

【Qt】(自制类)适用于QTextCharFormat的字体选择对话框
先附上github链接:https://github.com/Ls-Jan/Qt_CharFormatDialog 主要是作为QFontDialog的平替/增强,毕竟Qt自带的字体选择器一言难尽(用过的都叹气)。 【运行界面】 【功能】 一目了然,可以选择字体,设置字号,设置…...

Unity即时战略/塔防项目实战(一)——构造网格建造系统
Unity即时战略/塔防项目实战(一)—— 构造网格建造系统 效果展示 Unity RTS游戏网格建造系统实现原理 地形和格子划分,建造系统BuildManager构建 地形最终需要划分成一个一个的小方格,首先定义一下小方格: private…...
【ZOJ 1095】Humble Numbers 题解(动态规划)
一个素数只有2,3,5或7的数被称为谦逊数。序列1、2、3、4、5、6、7、8、9、10、12、14、15、16、18、20、21、24、25、27。。。显示了前20个不起眼的数字。 编写一个程序来查找并打印此序列中的第n个元素。 输入规范 输入由一个或多个测试用例组成。每个…...

百科媒体背书,什么媒体的收录可以修改百科?
传媒如春雨,润物细无声,大家好 大家都知道百科在百度搜索引擎中有很高的权重,排名非常靠前,任何机构,个人,或者企业做网络宣传百科是必不可少的,虽然任何人都可以注册并编辑其内容。但是&#x…...
USB鼠标实现——HID 报告的返回(八)
文章目录HID 报告的返回仓库地址USB 鼠标阅读顺序报告返回HID 报告的返回 仓库地址 仓库地址 USB 鼠标阅读顺序 枚举过程USB鼠标实现——设备描述符(一)USB鼠标实现——设置地址(二)USB鼠标实现——配置描述符集合(…...

DOPE PEG Maleimide,DOPE-PEG-Mal,二油酰磷脂酰乙醇胺PEG马来酰亚胺
文章关键词:高分子PEG,DOPE,聚乙二醇化修饰试剂基团反应特点: DOPE PEG Maleimide是一种由 DOPE 和马来酰亚胺基团组成的 PEG 化合物。基础产品数据: CAS号:N/A 中文名:1,2-二油酰-SN-甘油-3-磷…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...