mapbox 基于 Turf.js 实现高精度多边形分割(支持带空洞 / 坐标无损)

news2026/3/28 17:31:54
在 GIS 前端开发中多边形分割是高频需求如图斑拆分、地块划分。本文基于 Turf.js 封装了一套高精度多边形分割工具类支持普通模式 / 兼容模式可处理带空洞的多边形且能 100% 保留原始坐标避免 Turf.js 内置方法的坐标偏移问题。一、功能亮点双模式支持普通模式要求分割线与多边形外环恰好产生 2 个交点适用于精准分割场景。兼容模式自动过滤分割线保留与多边形交集部分支持≥2 个有效交点容错性更强。坐标无损切割纯手动切割算法完全保留原始坐标点无插值偏移适合对精度要求高的 GIS 系统。空洞自动归属分割后自动将原多边形的空洞分配到对应的子多边形中。完善的错误处理针对参数异常、交点不足等场景抛出明确错误便于调试。二、完整代码实现依赖确保项目已安装turf/turfnpm install turf/turf --save工具类代码import * as turf from turf/turf; class ChunkUtil { constructor() { this.turf turf; this.EPS 1e-8; this.tolerance 1e-10; } clipPolygon(polygon, polyline, compatibleMode false) { if (!polygon) throw new Error(未传入目标多边形); if (!polyline) throw new Error(未传入分割线); if (polygon.geometry?.type ! Polygon) throw new Error(必须是Polygon当前为${polygon.geometry?.type}); if (polyline.geometry?.type ! LineString) throw new Error(必须是LineString当前为${polyline.geometry?.type}); let finalLine polyline; if (compatibleMode) { finalLine this._filterLineToPolygon(polyline, polygon); } return this._setFloors(polygon, finalLine, compatibleMode); } _setFloors(polygonFeature, polylineFeature, compatibleMode) { const polygonBoundary this.turf.polygonToLine(polygonFeature); let boundaryLines []; if (polygonBoundary.geometry.type LineString) { boundaryLines [polygonBoundary]; } else if (polygonBoundary.geometry.type MultiLineString) { boundaryLines polygonBoundary.geometry.coordinates.map(coords this.turf.lineString(coords)); } else { throw new Error(不支持的几何类型${polygonBoundary.geometry.type}); } let targetBoundary null; let validIntersections []; for (const line of boundaryLines) { const intersections this.turf.lineIntersect(line, polylineFeature); if (compatibleMode) { validIntersections this._filterValidIntersections(intersections.features, polygonFeature); if (validIntersections.length 2) { targetBoundary line; validIntersections validIntersections.slice(0, 2); break; } } else { if (intersections.features.length 2) { targetBoundary line; validIntersections intersections.features; break; } } } if (!targetBoundary) { const errMsg compatibleMode ? 分割失败分割线未与多边形外环产生至少2个有效交点 : 分割失败分割线未与多边形外环产生恰好2个交点; throw new Error(errMsg); } return this._singleClip(targetBoundary, polylineFeature, polygonFeature, validIntersections, compatibleMode); } _singleClip(polygonBoundary, splitLine, originalPolygon, validIntersections, compatibleMode) { const { turf } this; const [p1, p2] validIntersections; const lineCoords splitLine.geometry.coordinates; const startPoint turf.point(lineCoords[0]); const endPoint turf.point(lineCoords[lineCoords.length - 1]); const startIn turf.booleanPointInPolygon(startPoint, originalPolygon); const endIn turf.booleanPointInPolygon(endPoint, originalPolygon); if (compatibleMode) { if (startIn endIn) throw new Error(分割线不能完全在内部); } else { if (startIn || endIn) throw new Error(分割线起点/终点不能在内部); } // --- 纯手动切割边界完全保留阶梯坐标 --- const fullRingCoords polygonBoundary.geometry.coordinates; const [clippedBoundaryCoords, remainingCoords] this._manualSplitRing(fullRingCoords, p1, p2); const clippedBoundary turf.lineString(clippedBoundaryCoords); const remainingLine turf.lineString(remainingCoords); // --- 纯手动切割分割线 --- const splitLineCoords splitLine.geometry.coordinates; const clippedSplitLineCoords this._manualSplitLine(splitLineCoords, p1, p2); const clippedSplitLine turf.lineString(clippedSplitLineCoords); // 拼接线段 const mergedLine1 this._connectLine(clippedBoundary, clippedSplitLine); mergedLine1.geometry.coordinates.push(mergedLine1.geometry.coordinates[0]); const polygon1Outer mergedLine1.geometry.coordinates; const mergedLine2 this._connectLine(remainingLine, clippedSplitLine); mergedLine2.geometry.coordinates.push(mergedLine2.geometry.coordinates[0]); const polygon2Outer mergedLine2.geometry.coordinates; // 空洞归属 const holes originalPolygon.geometry.coordinates.slice(1); const polygon1Holes [], polygon2Holes []; const tempPoly1 turf.polygon([polygon1Outer]); const tempPoly2 turf.polygon([polygon2Outer]); for (const hole of holes) { const center turf.centroid(turf.polygon([hole])); const in1 turf.booleanPointInPolygon(center, tempPoly1); const in2 turf.booleanPointInPolygon(center, tempPoly2); if (in1 !in2) polygon1Holes.push(hole); else if (in2 !in1) polygon2Holes.push(hole); else if (in1 in2) { const d1 turf.distance(center, p1); const d2 turf.distance(center, p2); d1 d2 ? polygon1Holes.push(hole) : polygon2Holes.push(hole); } } const polygon1 turf.polygon([polygon1Outer, ...polygon1Holes], originalPolygon.properties); const polygon2 turf.polygon([polygon2Outer, ...polygon2Holes], originalPolygon.properties); [polygon1, polygon2].forEach((poly, idx) { poly.properties { ...poly.properties, split: true, splitTime: Date.now(), splitId: ${originalPolygon.properties.gid || poly}_split_${idx} }; }); return turf.featureCollection([polygon1, polygon2]); } // --- 纯手动切割外环100%保留原始坐标 --- _manualSplitRing(ringCoords, p1, p2) { const ring [...ringCoords.slice(0, -1)]; // 移除闭合点 const insertPoint (pt) { for (let i 0; i ring.length; i) { const a ring[i], b ring[i 1]; if (this._isPointOnSegment(pt, a, b)) { ring.splice(i 1, 0, pt.geometry.coordinates); return; } } ring.push(pt.geometry.coordinates); }; insertPoint(p1); insertPoint(p2); const idx1 this._findPointIndex(ring, p1.geometry.coordinates); const idx2 this._findPointIndex(ring, p2.geometry.coordinates); let part1, part2; if (idx1 idx2) { part1 ring.slice(idx1, idx2 1); part2 ring.slice(idx2).concat(ring.slice(0, idx1 1)); } else { part1 ring.slice(idx2, idx1 1); part2 ring.slice(idx1).concat(ring.slice(0, idx2 1)); } return [part1, part2]; } // --- 纯手动切割分割线 --- _manualSplitLine(lineCoords, p1, p2) { const line [...lineCoords]; const insertPoint (pt) { for (let i 0; i line.length; i) { const a line[i], b line[i 1]; if (this._isPointOnSegment(pt, a, b)) { line.splice(i 1, 0, pt.geometry.coordinates); return; } } line.push(pt.geometry.coordinates); }; insertPoint(p1); insertPoint(p2); const idx1 this._findPointIndex(line, p1.geometry.coordinates); const idx2 this._findPointIndex(line, p2.geometry.coordinates); return idx1 idx2 ? line.slice(idx1, idx2 1) : line.slice(idx2, idx1 1); } _findPointIndex(coords, pt) { for (let i 0; i coords.length; i) { if (this.turf.distance(turf.point(coords[i]), turf.point(pt)) this.EPS) return i; } return -1; } _isPointOnSegment(pt, a, b) { const cross (pt.geometry.coordinates[0] - a[0]) * (b[1] - a[1]) - (pt.geometry.coordinates[1] - a[1]) * (b[0] - a[0]); if (Math.abs(cross) this.EPS) return false; const minX Math.min(a[0], b[0]), maxX Math.max(a[0], b[0]); const minY Math.min(a[1], b[1]), maxY Math.max(a[1], b[1]); return pt.geometry.coordinates[0] minX - this.EPS pt.geometry.coordinates[0] maxX this.EPS pt.geometry.coordinates[1] minY - this.EPS pt.geometry.coordinates[1] maxY this.EPS; } // --- 兼容模式辅助方法 --- _filterLineToPolygon(line, polygon) { const intersection this.turf.intersect(line, polygon); if (!intersection) throw new Error(分割线与当前图斑无交集); if (intersection.geometry.type MultiLineString) { const lines intersection.geometry.coordinates.map(coords this.turf.lineString(coords)); lines.sort((a, b) this.turf.length(b) - this.turf.length(a)); return lines[0]; } return intersection; } _filterValidIntersections(points, polygon) { const inPolygonPoints points.filter(point this.turf.booleanPointInPolygon(point, polygon)); const uniquePoints []; for (const p of inPolygonPoints) { const isDuplicate uniquePoints.some(u this.turf.distance(u, p) this.EPS); if (!isDuplicate) uniquePoints.push(p); } return uniquePoints; } /** * 提取多边形边界中未被切割的剩余坐标 * param {Object} fullBoundary - 完整多边形边界线LineString * param {Object} clippedBoundary - 切割后的边界线段LineString * returns {Array} 剩余坐标数组 * private */ _getRemainingBoundaryCoords(fullBoundary, clippedBoundary) { const fullCoords fullBoundary.geometry.coordinates; const clippedCoords clippedBoundary.geometry.coordinates; const isFirstPointMatch this._isPointInLine(turf.point(fullCoords[0]), clippedBoundary); // 1切割段在边界线头部/尾部 if (isFirstPointMatch) { return fullCoords.filter(coord !this._isCoordInArray(coord, clippedCoords)); } // 2切割段在边界线中间 else { let startPush false; let skipCount 0; const remainingCoords []; for (const coord of fullCoords) { if (!this._isCoordInArray(coord, clippedCoords)) { if (startPush) { remainingCoords.push(coord); } else { skipCount; } } else { startPush true; } } for (let i 0; i skipCount; i) { remainingCoords.push(fullCoords[i]); } return remainingCoords; } } _connectLine(line1, line2) { const l1End line1.geometry.coordinates[line1.geometry.coordinates.length - 1]; const l2Coords line2.geometry.coordinates; const l2Start l2Coords[0], l2End l2Coords[l2Coords.length - 1]; const merged [...line1.geometry.coordinates]; if (this.turf.distance(turf.point(l1End), turf.point(l2Start)) this.turf.distance(turf.point(l1End), turf.point(l2End))) { merged.push(...l2Coords.slice(1)); } else { merged.push(...l2Coords.reverse().slice(1)); } return this.turf.lineString(merged); } _isPointInLine(point, line) { return line.geometry.coordinates.some(coord this.turf.distance(turf.point(coord), point) this.EPS); } _isCoordInArray(coord, coordArray) { return coordArray.some(c this.turf.distance(turf.point(c), turf.point(coord)) this.EPS); } } const chunkUtil new ChunkUtil(); export function splitPolygon(targetPolygon, splitLine, compatibleMode false) { try { const splitResult chunkUtil.clipPolygon(targetPolygon, splitLine, compatibleMode); return splitResult?.features || null; } catch (error) { console.error(多边形分割出错, error); return null; } } export function createSplitLine(start, end) { if (!start || !end || start.length ! 2 || end.length ! 2) return null; return turf.lineString([start, end], { name: split-line }); }三、使用示例以 Vue3 mapbox 地图为例演示如何分割多边形template div refmapContainer classmap-container idmap/div /template script setup langts import mapboxgl from mapbox-gl import { ref, reactive, onMounted, defineProps, watch, onBeforeUnmount, computed, nextTick, inject } from vue import mapbox-gl/dist/mapbox-gl.css import MapboxDraw from mapbox/mapbox-gl-draw import mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css import baseService from ../../service/baseService let map; const mapContainer ref() // 分割相关状态 const isSplitting ref(false); // 标记是否处于分割模式 let splitSourceId split-temp-source; // 分割临时数据源ID let targetSplitFeature ref(null); // 待分割的目标图斑 const isDrawingSplitLine ref(false); // 分割入口方法 const segmentation () { console.log(进入分割); if (!map) { ElMessage.warning(地图未初始化完成无法分割); return; } // 1. 重置分割状态 resetSplitState(); // 2. 标记为分割模式 isSplitting.value true; ElMessage.info(请先点击选中需要分割的图斑); // 3. 绑定图斑选中事件 bindFeatureSelectEvent(handleSplitFeatureClick, 分割); }; // 重置分割状态 const resetSplitState () { resetDrawState({ isModeActive: isSplitting, isDrawingLine: isDrawingSplitLine, targetFeature: targetSplitFeature, drawCompleteHandler: handleSplitDrawComplete, drawModeChangeHandler: handleDrawModeChange, modeDesc: 分割 }, splitSourceId, clearHighlight1); }; // 处理分割模式下的图斑点击 const handleSplitFeatureClick (e) { if (isDrawingSplitLine.value) return; if (!isSplitting.value || !e.features || e.features.length 0) return; const feature e.features[0]; console.log(获取选中的图斑, feature); if (![Polygon, MultiPolygon].includes(feature.geometry.type)) { ElMessage.warning(仅支持多边形/多多边形图斑分割); return; } targetSplitFeature.value feature; ElMessage.info(已选中图斑[ID:${feature.properties.gid || 未知}]请绘制分割线双击结束); // 高亮图斑 highlightFeature1(feature); // 激活绘制模式 isDrawingSplitLine.value true; draw.changeMode(draw_line_string); // 监听Draw事件 map.off(draw.create, handleSplitDrawComplete); map.off(draw.modechange, handleDrawModeChange); map.on(draw.create, handleSplitDrawComplete); map.on(draw.modechange, handleDrawModeChange); }; // 监听Draw模式变化分割 const handleDrawModeChange (e) { if (e.mode simple_select) { isDrawingSplitLine.value false; } }; // 处理分割线绘制完成 const handleSplitDrawComplete (e) { try { if (!e || !e.features || e.features.length 0) { ElMessage.error(分割线绘制失败); resetSplitState(); return; } const drawedLineFeature e.features[0]; const splitLineId e.features[0].id; const standardSplitLine turf.lineString(drawedLineFeature.geometry.coordinates); // 调用分割方法 const splitResult splitPolygon(targetSplitFeature.value, standardSplitLine); console.log(分割数据, splitResult); if (!splitResult) { ElMessage.error(分割失败请确保分割线完全贯穿图斑); if (draw splitLineId) { draw.delete(splitLineId); console.log(分割失败已清除绘制的分割线); } resetSplitState(); return; } // 提交分割结果 submitSegmentation(targetSplitFeature.value, splitResult); ElMessage.success(分割成功); resetSplitState(); } catch (error) { console.error(分割处理失败, error); ElMessage.error(分割异常请重试); resetSplitState(); } }; // 提交分割结果 const submitSegmentation (oldData, newData) { const data { layerName: oldData.layer.id, gid: oldData.properties.gid, features: newData } //提交数据到后端 baseService.post(/data/boundary/trimming, data, { headers: { Content-Type: application/json } }).then(response { // if (response.data.status 200) { //更新分割结果到地图 // updateSplitPolygonDataSource(oldData.layer.id, newData); updateMapSourceWithPlasticResult(oldData.layer.id, newData, oldData.layer.gid) updateSegmentationShaping(分割) // } ElMessage.success({ type: success, message: response.data.message }) }).catch(err { ElMessage.error(失败, err); }) }; const updateSegmentationShaping (s) { if (map draw) { // 2. 用nextTick确保Vue状态更新完成 nextTick(() { try { // 3. 先切回选择模式强制 draw.changeMode(simple_select); console.log(当前Draw模式, draw.getMode()); // 验证是否切成功 // 4. 清空残留的分割线同步执行避免异步残留 draw.deleteAll(); // 5. 同步重置状态确保响应式更新 isDrawingSplitLine.value false; isDrawingTrimLine.value false; if (s 分割) { isSplitting.value true; // 6. 验证状态后再绑定事件避免空绑定 if (isSplitting.value) { console.log(重新分割状态, isSplitting.value); // 先解绑旧事件再绑定新事件避免重复绑定 map.off(click, handleSplitFeatureClick); bindFeatureSelectEvent(handleSplitFeatureClick, 分割); } } else if (s 整形) { isPlasticSurgery.value true if (isPlasticSurgery.value) { map.off(click, handlePlasticFeatureClick); bindFeatureSelectEvent(handlePlasticFeatureClick, 整形); } } } catch (err) { } }) } } /** * param {string} layerName - 原图层名称 * param {Array} plasticFeatures - 新图斑数据Feature数组 * param {string|number} gid - 图斑ID * 更新数据 */ const updateMapSourceWithPlasticResult async (layerName, plasticFeatures, gid) { try { closeDrawLayer(false) console.log(更新参数, { layerName, plasticFeatures, gid }); let layerIds map.getStyle().layers layerIds.forEach((item) { if (item.id.includes(layerName) !item.id.includes(_raster)) { map.setLayoutProperty(item.id, visibility, none); } }) setTimeout(() { layerIds.forEach((item) { if (item.id.includes(layerName) !item.id.includes(_raster)) { map.setLayoutProperty(item.id, visibility, visible); } }) }, 500) } catch (error) { ElMessage.error(提交失败${error.message}); } }; // 高亮分割图斑 const highlightFeature1 (feature) { // 复用公共高亮方法返回清除方法 clearHighlight1 highlightFeatureCommon(feature, split-highlight-layer, #ff9222); }; /** * 公共方法高亮图斑分割/整形共用 * param {Object} feature - 要高亮的图斑Feature * param {string} highlightLayerId - 高亮图层ID * param {string} color - 高亮颜色 */ const highlightFeatureCommon (feature, highlightLayerId, color) { // 清除旧高亮 const clearHighlight () { if (map.getLayer(highlightLayerId)) { map.removeLayer(highlightLayerId); } if (map.getSource(highlightLayerId)) { map.removeSource(highlightLayerId); } }; clearHighlight(); // 添加高亮数据源 map.addSource(highlightLayerId, { type: geojson, data: feature }); // 添加高亮图层 map.addLayer({ id: highlightLayerId, type: line, source: highlightLayerId, layout: { line-join: round, line-cap: round, line-simplification: none, antialias: false }, paint: { line-color: color, line-width: 2, line-opacity: 1 } }); return clearHighlight; // 返回清除方法 }; /** * 公共方法重置绘制相关状态分割/整形共用 * param {Object} stateConfig - 状态配置对象 * param {string} sourceId - 临时数据源ID * param {Function} clearHighlight - 清除高亮的方法 */ const resetDrawState (stateConfig, sourceId, clearHighlight) { // 重置状态变量 stateConfig.isModeActive.value false; stateConfig.isDrawingLine.value false; stateConfig.targetFeature.value null; // 清理Draw事件监听 if (map) { map.off(draw.create, stateConfig.drawCompleteHandler); map.off(draw.modechange, stateConfig.drawModeChangeHandler); } // 移除临时图层/数据源 if (map map.getSource(sourceId)) { map.removeSource(sourceId); } if (map map.getLayer(${sourceId}-layer)) { map.removeLayer(${sourceId}-layer); } // 清空Draw绘制要素 if (draw) { draw.deleteAll(); draw.changeMode(simple_select); console.log(重置${stateConfig.modeDesc}状态已清空Draw所有绘制要素); } // 清除高亮 clearHighlight(); }; // 全局计数器分割/整形分开 let splitCounter 0; // 公共排除图层规则分割/整形共用 const COMMON_EXCLUDE_LAYER_PATTERNS [ /^g-draw-/, // Draw绘图图层 /.cold$/, // Draw冷态图层 /-hover$/, // hover高亮图层 /_road$/, // 道路图层 /raster/, // 栅格底图 /outline/, // 轮廓图层 /city-label/, // 文字标注图层 /.hot$/, // Draw热态图层 /^guizhou/, // 贵州边界等非业务图层 point, // 点图层 polyline // 线图层 ]; /** * 公共方法过滤有效业务图层分割/整形共用 * returns {Array} 过滤后的有效图层ID数组 */ const getValidBusinessLayerIds () { if (!map) return []; const layerIds map.getStyle().layers.map(layer layer.id); return layerIds.filter(layerId { return !COMMON_EXCLUDE_LAYER_PATTERNS.some(pattern { if (pattern instanceof RegExp) { return pattern.test(layerId); } else { return layerId pattern; } }); }); }; /** * 公共方法绑定图斑选中事件分割/整形共用 * param {Function} clickHandler - 点击事件处理函数 * param {string} modeDesc - 模式描述分割/整形用于日志 */ const bindFeatureSelectEvent (clickHandler, modeDesc) { const validLayerIds getValidBusinessLayerIds(); // console.log(所有图层ID, map.getStyle().layers.map(layer layer.id)); // console.log(过滤后可交互的业务图层${modeDesc}, validLayerIds); validLayerIds.forEach((layerId) { // 先解绑旧事件避免重复绑定 map.off(click, layerId, clickHandler); // 绑定新的点击事件 map.on(click, layerId, clickHandler); console.log(为业务图层绑定${modeDesc}选中事件${layerId}); }); }; /script四、注意事项坐标系统确保多边形和分割线使用同一坐标系如 WGS84 或墨卡托否则会导致交点计算错误。交点要求普通模式下分割线必须与多边形外环恰好相交 2 次且起点 / 终点不能在多边形内部。兼容模式下分割线可部分在多边形内但需保证与外环有≥2 个有效交点。推荐使用turf/turf^6.5.0版本低版本可能存在 API 差异。效果展示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2458660.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…