cesium 实战系列之cesium风场
cesium风场最重要的其实是数据处理关于如何数据处理可以百度一下网上都有我这里的前提(prerequisite)是已经有了风场数据的情况下。1、创建风场的容器canvas idwind/canvas添加容器大小#wind { position: absolute; left: 0; top: 0; right: 0; bottom: 0; z-index: 4; pointer-events: none; }2、创建windy.js在index.html中引入全局引入var Windy function( params ){ var VELOCITY_SCALE 0.011; // scale for wind velocity (completely arbitrary--this value looks nice) var INTENSITY_SCALE_STEP 10; // step size of particle intensity color scale var MAX_TASK_TIME 1000; // amount of time before a task yields control (millis) var MIN_SLEEP_TIME 25; // amount of time a task waits before resuming (millis) var MAX_WIND_INTENSITY 40; // wind velocity at which particle intensity is maximum (m/s) var MAX_PARTICLE_AGE 100; // max number of frames a particle is drawn before regeneration var PARTICLE_LINE_WIDTH 1; // line width of a drawn particle var PARTICLE_MULTIPLIER 0.3; // particle count scalar (completely arbitrary--this values looks nice) scale: [5, 10, 15, 20, 25, 30, 35, 40] var PARTICLE_REDUCTION 0.75; // reduce particle count to this much of normal for mobile devices var FRAME_RATE 30; // desired milliseconds per frame var BOUNDARY 0.45; var NULL_WIND_VECTOR [NaN, NaN, null]; // singleton for no wind in the form: [u, v, magnitude] var TRANSPARENT_BLACK [255, 0, 0, 0]; var τ 2 * Math.PI; var H Math.pow(10, -5.2); var globe window.cesiumGlobe; // interpolation for vectors like wind (u,v,m) var bilinearInterpolateVector function(x, y, g00, g10, g01, g11) { var rx (1 - x); var ry (1 - y); var a rx * ry, b x * ry, c rx * y, d x * y; var u g00[0] * a g10[0] * b g01[0] * c g11[0] * d; var v g00[1] * a g10[1] * b g01[1] * c g11[1] * d; return [u, v, Math.sqrt(u * u v * v)]; }; var createWindBuilder function(uComp, vComp) { var uData uComp.data, vData vComp.data; return { header: uComp.header, //recipe: recipeFor(wind- uComp.header.surface1Value), data: function(i) { return [uData[i], vData[i]]; }, interpolate: bilinearInterpolateVector } }; var createBuilder function(data) { var uComp null, vComp null, scalar null; data.forEach(function(record) { switch (record.header.parameterCategory , record.header.parameterNumber) { case 2,2: uComp record; break; case 2,3: vComp record; break; default: scalar record; } }); return createWindBuilder(uComp, vComp); }; var buildGrid function(data, callback) { var builder createBuilder(data); var header builder.header; var λ0 header.lo1, φ0 header.la1; // the grids origin (e.g., 0.0E, 90.0N) var Δλ header.dx, Δφ header.dy; // distance between grid points (e.g., 2.5 deg lon, 2.5 deg lat) var ni header.nx, nj header.ny; // number of grid points W-E and N-S (e.g., 144 x 73) var date new Date(header.refTime); date.setHours(date.getHours() header.forecastTime); // Scan mode 0 assumed. Longitude increases from λ0, and latitude decreases from φ0. // http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_table3-4.shtml var grid [], p 0; var isContinuous Math.floor(ni * Δλ) 360; for (var j 0; j nj; j) { var row []; for (var i 0; i ni; i, p) { row[i] builder.data(p); } if (isContinuous) { // For wrapped grids, duplicate first column as last column to simplify interpolation logic row.push(row[0]); } grid[j] row; } function interpolate(λ, φ) { var i floorMod(λ - λ0, 360) / Δλ; // calculate longitude index in wrapped range [0, 360) var j (φ0 - φ) / Δφ; // calculate latitude index in direction 90 to -90 var fi Math.floor(i), ci fi 1; var fj Math.floor(j), cj fj 1; var row; if ((row grid[fj])) { var g00 row[fi]; var g10 row[ci]; if (isValue(g00) isValue(g10) (row grid[cj])) { var g01 row[fi]; var g11 row[ci]; if (isValue(g01) isValue(g11)) { // All four points found, so interpolate the value. return builder.interpolate(i - fi, j - fj, g00, g10, g01, g11); } } } return null; } callback( { date: date, interpolate: interpolate }); }; /** * returns {Boolean} true if the specified value is not null and not undefined. */ var isValue function(x) { return x ! null x ! undefined; } /** * returns {Number} returns remainder of floored division, i.e., floor(a / n). Useful for consistent modulo * of negative numbers. See http://en.wikipedia.org/wiki/Modulo_operation. */ var floorMod function(a, n) { return a - n * Math.floor(a / n); } /** * returns {Number} the value x clamped to the range [low, high]. */ var clamp function(x, range) { return Math.max(range[0], Math.min(x, range[1])); } /** * returns {Boolean} true if agent is probably a mobile device. Dont really care if this is accurate. */ var isMobile function() { return (/android|blackberry|iemobile|ipad|iphone|ipod|opera mini|webos/i).test(navigator.userAgent); } /** * Calculate distortion of the wind vector caused by the shape of the projection at point (x, y). The wind * vector is modified in place and returned by this function. */ var distort function(projection, λ, φ, x, y, scale, wind) { var u wind[0] * scale; var v wind[1] * scale; var d distortion(projection, λ, φ, x, y); // Scale distortion vectors by u and v, then add. wind[0] d[0] * u d[2] * v; wind[1] d[1] * u d[3] * v; return wind; }; var distortion function(projection, λ, φ, x, y) { var τ 2 * Math.PI; var G 36e-6; var i λ 0 ? G : -G , a φ 0 ? G : -G , u projection([λ i, φ]) , c projection([λ, φ a]) , s Math.cos(φ / 360 * τ); return [(u[0] - x) / i / s, (u[1] - y) / i / s, (c[0] - x) / a, (c[1] - y) / a]; } var createField function(columns, bounds, callback) { /** * returns {Array} wind vector [u, v, magnitude] at the point (x, y), or [NaN, NaN, null] if wind * is undefined at that point. */ function field(x, y) { var column columns[Math.round(x)]; return column column[Math.round(y)] || NULL_WIND_VECTOR; } /** * returns {boolean} true if the field is valid at the point (x, y) */ field.isDefined function(x, y) { return field(x, y)[2] ! null; }; /** * returns {boolean} true if the point (x, y) lies inside the outer boundary of the vector field, even if * the vector field has a hole (is undefined) at that point, such as at an island in a field of * ocean currents. */ field.isInsideBoundary function(x, y) { return field(x, y) ! NULL_WIND_VECTOR; }; // Frees the massive columns array for GC. Without this, the array is leaked (in Chrome) each time a new // field is interpolated because the field closures context is leaked, for reasons that defy explanation. field.release function() { columns []; }; field.randomize function(o) { // UNDONE: this method is terrible var x, y; var safetyNet 0; do { x Math.round(Math.floor(Math.random() * bounds.width) bounds.x); y Math.round(Math.floor(Math.random() * bounds.height) bounds.y) } while (field(x, y)[2] null safetyNet 30); o.x x; o.y y; return o; }; //field.overlay mask.imageData; //return field; callback( bounds, field ); }; var buildBounds function( bounds, width, height ) { var upperLeft bounds[0]; var lowerRight bounds[1]; var x Math.round(upperLeft[0]); //Math.max(Math.floor(upperLeft[0], 0), 0); var y Math.max(Math.floor(upperLeft[1], 0), 0); var xMax Math.min(Math.ceil(lowerRight[0], width), width - 1); var yMax Math.min(Math.ceil(lowerRight[1], height), height - 1); return {x: x, y: y, xMax: width, yMax: yMax, width: width, height: height}; }; var deg2rad function( deg ){ return (deg / 180) * Math.PI; }; var rad2deg function( ang ){ return ang / (Math.PI/180.0); }; var invert function(x, y, windy){ var mapLonDelta windy.east - windy.west; var worldMapRadius windy.width / rad2deg(mapLonDelta) * 360/(2 * Math.PI); var mapOffsetY ( worldMapRadius / 2 * Math.log( (1 Math.sin(windy.south) ) / (1 - Math.sin(windy.south)) )); var equatorY windy.height mapOffsetY; var a (equatorY-y)/worldMapRadius; var lat 180/Math.PI * (2 * Math.atan(Math.exp(a)) - Math.PI/2); var lon rad2deg(windy.west) x / windy.width * rad2deg(mapLonDelta); return [lon, lat]; }; var mercY function( lat ) { return Math.log( Math.tan( lat / 2 Math.PI / 4 ) ); }; var project function( lat, lon, windy) { // both in radians, use deg2rad if neccessary var ymin mercY(windy.south); var ymax mercY(windy.north); var xFactor windy.width / ( windy.east - windy.west ); var yFactor windy.height / ( ymax - ymin ); var y mercY( deg2rad(lat) ); var x (deg2rad(lon) - windy.west) * xFactor; var y (ymax - y) * yFactor; // y points south return [x, y]; }; function calcSparseFactor(globe) { var viewRect globe.viewRect(); var minRange Math.min(Math.abs(viewRect.east - viewRect.west), Math.abs(viewRect.north - viewRect.south)); var factor Math.sqrt(minRange / 90); return factor; } var interpolateField function( grid, bounds, /*extent,*/ callback ) { var projection globe.cesiumWGS84ToWindowCoord; // How fast particles move on the screen (arbitrary value chosen for aesthetics). var sparseFactor calcSparseFactor(globe); var scale 1/40000; var velocityScale bounds.height * scale * Math.min(3.0, sparseFactor); //var velocityScale VELOCITY_SCALE; var columns []; var x bounds.x; function interpolateColumn(x) { var column []; for (var y bounds.y; y bounds.yMax; y 2) { //var coord invert( x, y, extent ); var coord globe.cesiumWindowToWGS84(x, y); if (coord) { var λ coord[0], φ coord[1]; if (isFinite(λ)) { var wind grid.interpolate(λ, φ); if (wind) { wind distort(projection, λ, φ, x, y, velocityScale, wind/*, extent*/); column[y1] column[y] wind; } } } } columns[x1] columns[x] column; } (function batchInterpolate() { var start Date.now(); while (x bounds.width) { interpolateColumn(x); x 2; if ((Date.now() - start) MAX_TASK_TIME) { setTimeout(batchInterpolate, MIN_SLEEP_TIME); return; } } createField(columns, bounds, callback); })(); }; var animate function(bounds, field) { function asColorStyle(r, g, b, a) { return rgba( 243 , 243 , 238 , a ); } function hexToR(h) {return parseInt((cutHex(h)).substring(0,2),16)} function hexToG(h) {return parseInt((cutHex(h)).substring(2,4),16)} function hexToB(h) {return parseInt((cutHex(h)).substring(4,6),16)} function cutHex(h) {return (h.charAt(0)#) ? h.substring(1,7):h} function windIntensityColorScale(step, maxWind) { var result [ //blue to red rgba( hexToR(#178be7) , hexToG(#178be7) , hexToB(#178be7) , 1 ), rgba( hexToR(#8888bd) , hexToG(#8888bd) , hexToB(#8888bd) , 1 ), rgba( hexToR(#b28499) , hexToG(#b28499) , hexToB(#b28499) , 1 ), rgba( hexToR(#cc7e78) , hexToG(#cc7e78) , hexToB(#cc7e78) , 1 ), rgba( hexToR(#de765b) , hexToG(#de765b) , hexToB(#de765b) , 1 ), rgba( hexToR(#ec6c42) , hexToG(#ec6c42) , hexToB(#ec6c42) , 1 ), rgba( hexToR(#f55f2c) , hexToG(#f55f2c) , hexToB(#f55f2c) , 1 ), rgba( hexToR(#fb4f17) , hexToG(#fb4f17) , hexToB(#fb4f17) , 1 ), rgba( hexToR(#fe3705) , hexToG(#fe3705) , hexToB(#fe3705) , 1 ), rgba( hexToR(#ff0000) , hexToG(#ff0000) , hexToB(#ff0000) , 1 ) result.indexFor function(m) { // map wind speed to a style return Math.floor(Math.min(m, maxWind) / maxWind * (result.length - 1)); }; return result; } var colorStyles windIntensityColorScale(INTENSITY_SCALE_STEP, MAX_WIND_INTENSITY); //var colorStyles µ.windIntensityColorScale(INTENSITY_SCALE_STEP, 17); var buckets colorStyles.map(function() { return []; }); var particleCount Math.round(bounds.width * PARTICLE_MULTIPLIER); // console.log(particleCount); if (isMobile()) { particleCount * PARTICLE_REDUCTION; } var fadeFillStyle rgba(255, 0, 0, 0.95); var particles []; for (var i 0; i particleCount; i) { particles.push(field.randomize({age: Math.floor(Math.random() * MAX_PARTICLE_AGE) 0})); } function evolve() { buckets.forEach(function(bucket) { bucket.length 0; }); particles.forEach(function(particle) { if (particle.age MAX_PARTICLE_AGE) { field.randomize(particle).age 0; } var x particle.x; var y particle.y; var v field(x, y); // vector at current position var m v[2]; if (m null) { particle.age MAX_PARTICLE_AGE; // particle has escaped the grid, never to return... } else { /*var xt x v[0]; var yt y v[1];*/ var xt x v[0]/3.0; var yt y v[1]/3.0; if (field(xt, yt)[2] ! null) { // Path from (x,y) to (xt,yt) is visible, so add this particle to the appropriate draw bucket. particle.xt xt; particle.yt yt; buckets[colorStyles.indexFor(m)].push(particle); } else { // Particle isnt visible, but it still moves through the field. particle.x xt; particle.y yt; } } particle.age 1; }); } var g params.canvas.getContext(2d); g.lineWidth PARTICLE_LINE_WIDTH; g.fillStyle fadeFillStyle; function draw() { // Fade existing particle trails. var prev g.globalCompositeOperation; g.globalCompositeOperation destination-in; g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); g.globalCompositeOperation prev; // Draw new particle trails. buckets.forEach(function(bucket, i) { if (bucket.length 0) { g.beginPath(); g.strokeStyle colorStyles[i]; //g.strokeStyle rgba(224,102,255,0.5); bucket.forEach(function(particle) { g.moveTo(particle.x, particle.y); g.lineTo(particle.xt, particle.yt); particle.x particle.xt; particle.y particle.yt; }); g.stroke(); } }); } (function frame() { try { windy.timer setTimeout(function() { requestAnimationFrame(frame); evolve(); draw(); }, 1000 / FRAME_RATE); } catch (e) { console.error(e); } })(); } var start function( bounds, width, height /*, extent */){ stop(); // build grid (params.data,callback); /*callback( { date: date, interpolate: interpolate });*/ buildGrid( params.data, function(grid){ // interpolateField interpolateField( grid, buildBounds( bounds, width, height), /*mapBounds,*/ function( bounds, field ){ // animate the canvas with random points windy.field field; animate( bounds, field ); }); }); return true; }; var stop function(){ if (windy.field) windy.field.release(); if (windy.timer) clearTimeout(windy.timer) }; var windy { params: params, start: start, stop: stop }; return windy; } // shim layer with setTimeout fallback window.requestAnimationFrame (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 50); }; })();3、创建initWindy.js在页面中引入var windy var started false function Draw() { $.ajax({ type: get, url: xxxxxxxxxxxxxxx, headers:{}, dataType: json, success: function (response) { windy new Windy({ canvas: document.getElementById(wind), data: response }) }, error: function (errorMsg) { } }) } function initWindy() { window.cesiumGlobe globe(viewer) viewer._cesiumWidget._creditContainer.style.display none $(#wind)[0].width parseInt(viewer.canvas.width) $(#wind)[0].height parseInt(viewer.canvas.height) // 加载风场json数据转换成后台接口 Draw() viewer.camera.moveStart.addEventListener(function () { // console.log(move start...) if (!!windy started) { $(#wind).hide() windy.stop() } }) viewer.camera.moveEnd.addEventListener(function () { // console.log(move end...) if (!!windy started) { $(#wind).hide() redraw() } }) } function redraw() { //风场重新绘制 var width viewer.canvas.width var height viewer.canvas.height $(#wind)[0].width width $(#wind)[0].height height windy.stop() setTimeout(function () { started windy.start( [ [0, 0], [width, height] ], width, height ) $(#wind).show() }, 50) } function stopWindyPlay() { //隐藏风场 $(#wind).hide() if (windy) { windy.stop() started false } } function startWindyPlay() { //显示风场 $(#wind).hide() Draw() redraw() } export { initWindy, Draw, redraw, stopWindyPlay, startWindyPlay }4、在已创建好cesium地图的情况下页面初始化时调用 初始化风场mounted() { this.$nextTick(() { initWindy(); }); },效果如图风场

相关新闻

行业语言大模型会怎样改变办公协作与企业效率

行业语言大模型会怎样改变办公协作与企业效率

一、为什么很多人开始重新理解大模型的价值 这两年,大模型已经从“能不能用”走到了“怎么用得更稳、更准、更省”的阶段。早期很多人对大模型的印象,停留在聊天、写文案、做搜索总结这些偏通用的能力上。但真正进入企业场景后,大家很快发现一…

2026/8/12 18:17:24 阅读更多 →
小芯片,大存储 | XT25F32F-S深度解析

小芯片,大存储 | XT25F32F-S深度解析

你有没有想过:智能门锁断电之后,指纹数据为什么还在?路由器重启之后,配置为什么没丢?智能电表没联网的时候,计量数据存在哪里?答案是一颗你可能从没注意过的芯片——NOR Flash。它和U盘不是一回…

2026/8/12 18:17:24 阅读更多 →
小芯片,大能量:XT25Q128F SPI NOR Flash 深度解读

小芯片,大能量:XT25Q128F SPI NOR Flash 深度解读

引言:无处不在的“数据心脏”在智能化时代,我们身边的每一台电子设备都在不停运转——智能音箱在听我们说话,门锁在验证指纹,汽车仪表盘在实时显示车速……这些设备之所以能“思考”,离不开一颗默默工作的存储芯片。今…

2026/8/12 18:17:24 阅读更多 →

最新新闻

Unity渲染中Dither透明物体阴影丢失的深度解析与解决方案

Unity渲染中Dither透明物体阴影丢失的深度解析与解决方案

1. 项目概述:当透明物体的阴影在Dither下“神秘消失” 做Unity渲染开发,尤其是涉及到半透明效果时,最让人头疼的莫过于“场景里看着好好的,一运行阴影就没了”。如果你正在使用Dither(抖动)技术来处理透明物…

2026/8/12 20:33:36 阅读更多 →
如何用VideoDownloadHelper轻松下载网页视频:新手必备的完整指南

如何用VideoDownloadHelper轻松下载网页视频:新手必备的完整指南

如何用VideoDownloadHelper轻松下载网页视频:新手必备的完整指南 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 还在为无法保存网…

2026/8/12 20:33:36 阅读更多 →
figma可动眼人偶深度解析:从结构原理到摄影实战

figma可动眼人偶深度解析:从结构原理到摄影实战

这次我们来看一个非常特别的“美少女肥姑妈”新品——figma 托洛洛&可露凯。对于熟悉可动人偶的玩家来说,figma系列一直以高可动性和丰富的配件著称,但这次的新品带来了一个突破性的亮点: 首次搭载了可动眼结构 。这意味着玩家可以手动…

2026/8/12 20:33:36 阅读更多 →
2026年GEO优化服务商选型指南:科学评估与精准匹配

2026年GEO优化服务商选型指南:科学评估与精准匹配

当用户向AI提问“哪个品牌更值得选”时,答案里出现的是你的品牌,还是竞争对手的名字?这个看似简单的问题,正在成为决定企业线上获客效率的核心变量。GEO(生成式引擎优化)已从营销可选项升级为品牌必选项&am…

2026/8/12 20:33:36 阅读更多 →
【notes8】usb

【notes8】usb

文章目录1.硬件接口2.枚举和断开过程usb调试PC-USBUSB-SMD1.硬件接口 USB口用于打印机,第四个是硬盘: OTG:On The Go(安上即可用): 如下是typec接口,usb2.0协议只关心红色的框。 2.枚举和断…

2026/8/12 20:33:36 阅读更多 →
ICC2学习笔记之Clock Tree Synthesis

ICC2学习笔记之Clock Tree Synthesis

参考资料ICC2 User Guide Version M-2016.12-SP4• Prerequisites for Clock Tree Synthesis做时钟树的先决条件:1.定义了create_clock或create_generated_clock2.已经placement且合法化且满足QoR,满足QoR包括:Congestion;Timing&…

2026/8/12 20:32:36 阅读更多 →

日新闻

Ubuntu 22.04安装与使用tree命令:高效管理Linux目录结构

Ubuntu 22.04安装与使用tree命令:高效管理Linux目录结构

1. 为什么需要一个“目录树”工具?在Linux世界里,尤其是Ubuntu这样的发行版,命令行是很多人的主战场。我们每天都要和文件、目录打交道。ls命令是查看目录内容的首选,它简洁、高效,能列出文件名、权限、大小等关键信息…

2026/8/12 9:33:34 阅读更多 →
博思AI智能体:意图识别、思考链与性能优化的工程实践

博思AI智能体:意图识别、思考链与性能优化的工程实践

在AI应用从“能用”走向“好用”的进程中,系统的响应速度、决策透明度与高并发稳定性是决定用户体验的关键。博思AI智能体近期完成了一次重要的专项优化,聚焦于意图识别、思考链展示与全链路压测三大核心领域,将系统从功能实现推向了工程卓越…

2026/8/12 9:33:34 阅读更多 →
子代理架构:AI智能体任务分解与协同执行的核心原理与实践

子代理架构:AI智能体任务分解与协同执行的核心原理与实践

1. 项目概述:为什么我们需要“子代理”?最近在折腾各种AI应用和自动化流程时,我越来越频繁地遇到一个瓶颈:单个AI智能体(Agent)的能力边界。无论是处理复杂的多步骤任务,还是需要同时调用多个专…

2026/8/12 9:33:34 阅读更多 →

周新闻

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁 【免费下载链接】baidupankey 在线查询网盘提取码(维护中 rm repo) 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 你是否曾经在深夜寻找一份重要资料&#x…

2026/8/12 1:11:09 阅读更多 →
如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/12 1:11:09 阅读更多 →
收藏!小白程序员轻松入门大模型,从Harness工程开始实践

收藏!小白程序员轻松入门大模型,从Harness工程开始实践

文章强调学习大模型不应只关注模型本身,而应重视模型外的系统搭建,即Harness。提出AgentModelHarness的实用公式,详细介绍Harness的四个层次:持久化层、执行层、控制层和观察与验证层。文章还探讨了上下文工程、工具设计、AGENTS.…

2026/8/12 1:11:08 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/11 17:09:45 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/12 1:11:10 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/11 17:09:45 阅读更多 →