此系列已完结,共3部分:
本来说有时间就把这个项目完结了的,结果后面一直有事拖着,直到现在十一月份了才搞完。老样子,先看成果。
后面可能审美疲劳了,越看越丑,就干脆直接用dataV(这可不是阿里的那个dataV)修饰页面了。这是项目改动后的样子:





主要做了如下改动:
粒子球体也是用three.js完成的,放大仔细看会发现其实是有三层构成的:内层透明球体,中间点状球体,外层病毒球体。

具体实现过程是这样的:
//创建斑点球体
async function createSpotSphere() {
let globeBufferGeometry = new THREE.SphereGeometry(earthSize - 1, 50, 50);//球体几何体
let globeInnerMaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(dvColor[0]),//颜色
// blending: THREE.AdditiveBlending,//纹理融合的叠加方式
// side: THREE.FrontSide,//前面显示
transparent: true,//透明
// depthWrite: false,//深度写入
// depthTest: false,//黑洞效果
opacity: .3,//不透明度
});
let globeInnerMesh = new THREE.Mesh(
globeBufferGeometry,
globeInnerMaterial
);
earthGroup.add(globeInnerMesh); //将网格放入地球组
cre
let img = new Image();
img.src = earthGrayscale; //黑白地图
将图片使用onload加载到项目中后,利用canvas绘制一遍该图,再使用getImageData获取到像素点数据canData。
let canvas = document.createElement("canvas");
canvas.width = img.width; //使得canvas尺寸与图片尺寸相同
canvas.height = img.height;
(canvas.getContext("2d") as any).drawImage(img, 0, 0, img.width, img.height);//canvas绘制图片
let canData = (canvas.getContext("2d") as any).getImageData(0, 0, canvas.width, canvas.height);//获取画布像素数据
利用canData .data中的rgba信息生成缓冲几何顶点数组globeCloudVerticesArray。
let globeCloudBufferGeometry = new THREE.BufferGeometry(); //设置缓冲几何体
let globeCloudVerticesArray = []; //地球云缓冲几何体顶点
let o = null; //数组处理时的计数
for (o = 0; o < canData.data.length; o += 4) {
let r = (o / 4) % canvas.width,
i = (o / 4 - r) / canvas.width;
if ((o / 4) % 2 == 1 && i % 2 == 1)
if (0 === canData.data[o]) {
let n = r,
longitude = (i / (canvas.height / 180) - 90) / -1, //经度
latitude = n / (canvas.width / 360) - 180; //维度
let s = latLongToVector3(longitude, latitude, earthSize, .1); //经纬度变换
globeCloudVerticesArray.push(s); //将变换后的顶点放入数组
}
}
然后再使用three中的BufferAttribute生成属性position与color。
let l = new Float32Array(3 * globeCloudVerticesArray.length); //创建顶点数组长度
for (o = 0; o < globeCloudVerticesArray.length; o++) {
l[3 * o] = globeCloudVerticesArray[o].x;//设置顶点数组数据
l[3 * o + 1] = globeCloudVerticesArray[o].y;
l[3 * o + 2] = globeCloudVerticesArray[o].z;
}
let positionVal = new THREE.BufferAttribute(l, 3); //设置缓冲区属性值
globeCloudBufferGeometry.setAttribute("position", positionVal); //给缓冲几何体添加位置属性
let globeCloudMaterial = new THREE.PointsMaterial({
color: new THREE.Color(dvColor[1]),//颜色
fog: true,
size: 1,
});//球面斑点材质
let d = new Float32Array(3 * globeCloudVerticesArray.length), c = [];
for (o = 0; o < globeCloudVerticesArray.length; o++) {
c[o] = new THREE.Color(dvColor[1]);//球面斑点颜色
d[3 * o] = c[o].r;//设置地球云数组rgb颜色
d[3 * o + 1] = c[o].g;
d[3 * o + 2] = c[o].b;
}
let color_val = new THREE.BufferAttribute(d, 3);
globeCloudBufferGeometry.setAttribute("color", color_val);//给缓冲几何体添加颜色属性,修改颜色直接修改globeCloudBufferGeometry的setAttribute
最后再使用THREE.Points创建球面的点,将position与color属性添加到点的几何体BufferGeometry中。
let globeCloud = new THREE.Points(//球面的象素点
globeCloudBufferGeometry,
globeCloudMaterial
);
这是需要用到的坐标转换方法:
//经纬度坐标变换(传入e:纬度、a经度、t球半径、o球额外距离)
function latLongToVector3(e: any, a: any, t: any, o: any) {
var r = (e * Math.PI) / 180,
i = ((a - 180) * Math.PI) / 180,
n = -(t + o) * Math.cos(r) * Math.cos(i),
s = (t + o) * Math.sin(r),
l = (t + o) * Math.cos(r) * Math.sin(i);
return new THREE.Vector3(n, s, l); //计算三维向量
};
//创建病毒
function createVirus(data: any, earthSize: any) {
let colors = [
new THREE.Color(0xf9b8b8),
new THREE.Color(0xfe4242),
new THREE.Color(0xff0000),
]; //病毒颜色列表
let virSize = 4; //病毒大小
let list = JSON.parse(JSON.stringify(data));
list.forEach((e: { value: number; color: any; position: any[]; }) => {
e.value >= 10000000 && (e.color = colors[2]); //根据病毒数赋予不同颜色
e.value >= 500000 && e.value < 10000000 && (e.color = colors[1]);
e.value < 500000 && (e.color = colors[0]);
if (e.position) {
let virusMaterial = new THREE.SpriteMaterial({
color: e.color,
map: new THREE.TextureLoader().load(virusImg),
side: THREE.FrontSide, //只显示前面
}); //病毒材质
let Sprite = new THREE.Sprite(virusMaterial); //点精灵材质
Sprite.scale.set(virSize, virSize, 1); //点大小
let lat = e.position[1]; //纬度
let lon = e.position[0]; //经度
let s = latLongToVector3(lat, lon, earthSize, 1); //坐标转换
Sprite.position.set(s.x, s.y, s.z); //设置点的位置
Sprite.dotData = e; //将点的数据添加到dotData属性中
Sprite.name = "病毒";
earthGroup.add(Sprite); //将病毒添加进球体组中
}
});
};


let option = {
title: {
text: provinceBaseData.value.province + "各地数据",
left: "center",
top: '5%',
textStyle: {
color: "#fff",
},
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
dataZoom: [
{
type: 'inside',
},
{
show: true,
yAxisIndex: 0,
filterMode: 'empty',
width: 25,
height: '70%',
showDataShadow: false,
left: '3%',
top: "center"
}
],
legend: {
data: ['累计数', '治愈数', '确诊数', '较昨日新增', '死亡数'],
orient: "vertical",
top: "15%",
right: "2%",
textStyle: {
color: "#fff"
},
},
grid: {
left: '3%',
right: '15%',
bottom: '10%',
containLabel: true
},
xAxis: {
type: 'category',
data: echartData.cityName,
axisLabel: {
interval: 0,
rotate: 50,
color: "#fff"
},
},
yAxis: {
type: 'value',
axisLabel: {
color: "#fff",
},
},
series: [
{
name: '累计数',
type: 'bar',
emphasis: {
focus: 'series'
},
itemStyle: {
color: '#f59158'
},
data: echartData.conNum
},
{
name: '治愈数',
type: 'bar',
emphasis: {
focus: 'series'
},
itemStyle: {
color: '#48c56b'
},
data: echartData.cureNum
},
{
name: '确诊数',
type: 'bar',
stack: 'total',
emphasis: {
focus: 'series'
},
itemStyle: {
color: '#ffd889'
},
data: echartData.econNum
},
{
name: '较昨日新增',
type: 'bar',
stack: 'total',
emphasis: {
focus: 'series'
},
itemStyle: {
color: '#794ebd'
},
data: echartData.asymptomNum
},
{
name: '死亡数',
type: 'bar',
stack: 'total',
emphasis: {
focus: 'series'
},
itemStyle: {
color: '#ff6a6a'
},
data: echartData.deathNum
},
]
};

let option = {
title: {
text: provinceBaseData.value.province + "历史数据",
left: "center",
top: '5%',
textStyle: {
color: "#fff",
},
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
legend: {
data: ['累计数', '确诊数', '较昨日新增', '治愈数', '死亡数'],
orient: "vertical",
top: "15%",
right: "2%",
textStyle: {
color: "#fff"
},
},
grid: {
left: '8%',
},
xAxis: [
{
type: 'category',
boundaryGap: false,
axisLabel: {
color: "#fff",
},
data: echatrData.time
}
],
yAxis: [
{
type: 'value',
axisLabel: {
color: "#fff",
},
}
],
dataZoom: [
{
startValue: ''
},
{
type: 'inside'
}
],
series: [
{
name: '累计数',
type: 'line',
stack: 'Total',
smooth: true,
lineStyle: {
width: 0
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: "#f59158"
},
emphasis: {
focus: 'series'
},
itemStyle: {
color: '#f59158'
},
data: echatrData.conNum
},
{
name: '确诊数',
type: 'line',
stack: 'Total',
smooth: true,
lineStyle: {
width: 0
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: "#ffd889"
},
itemStyle: {
color: '#ffd889'
},
emphasis: {
focus: 'series'
},
data: echatrData.econNum
},
{
name: '较昨日新增',
type: 'line',
stack: 'Total',
smooth: true,
lineStyle: {
width: 0
},
showSymbol: false,
label: {
show: true,
position: 'top'
},
areaStyle: {
opacity: 0.8,
color: "#794ebd"
},
itemStyle: {
color: '#794ebd'
},
emphasis: {
focus: 'series'
},
data: echatrData.asymptomNum
},
{
name: '治愈数',
type: 'line',
stack: 'Total',
smooth: true,
lineStyle: {
width: 0
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: "#48c56b"
},
itemStyle: {
color: '#48c56b'
},
emphasis: {
focus: 'series'
},
data: echatrData.cureNum
},
{
name: '死亡数',
type: 'line',
stack: 'Total',
smooth: true,
lineStyle: {
width: 0
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: "#ff6a6a"
},
itemStyle: {
color: '#ff6a6a'
},
emphasis: {
focus: 'series'
},
data: echatrData.deathNum
},
]
};
报告生成利用的是docxtemplater,这是前端生成word比较方便的一个插件,具体使用方法可以看这里:https://www.cnblogs.com/xi12/p/16863383.html。

我即将开始一个将录制和编辑音频文件的项目,我正在寻找一个好的库(最好是Ruby,但会考虑Java或.NET以外的任何库)以进行实时可视化波形。有人知道我应该从哪里开始搜索吗? 最佳答案 要流入浏览器的数据量很大。Flash或Flex图表可能是唯一能提高内存效率的解决方案。Javascript图表往往会因大型数据集而崩溃。 关于ruby-Ruby中的波形可视化,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.c
Unity数据可视化图表插件XCharts3.0发布历时8个多月,业余时间,断断续续,XCharts3.0总算发布了。如果要打个满意度,我给3.0版本来个80分。对于代码框架结构设计的调整改动,基本符合预期,甚是满意。相比之前的1.0和2.0版本,我认为3.0才是一个拿得出手给广大开发者使用的版本。1.0发布的时候,很兴奋,从0.1到1.0,也磨了一年,真的等不及想给大家试用了,还特地写过一篇文章以示庆祝。那个时候,1.0虽然还还不够完善,功能也不够丰富,但它是XCharts的开始,没有1.0,也就没有后面的2.0和3.0。后面的2.0发布,做了很多改进和优化,随着版本迭代,慢慢的发现有不少硬
本人是音乐爱好者,从小就特别喜欢那个随着音乐跳动的方框效果,就是这个:arduino上一大把对,我忍你很久了,我就想用mpy做,全网没有,行我自己研究。果然兴趣是最好的老师,我之前有篇博客专门讲音频,有兴趣的可以回顾一下。提到可视化频谱,必然绕不开fft,大学学过这玩意,当时一心玩,老师讲的一个字都么听进去,网上教程简略扫了一下,大该就是把时域转频域的工具,我大mpy居然没有fft函数,奶奶的,先放着。音频信息如何收集?第一种傻瓜式的ADC,模拟转数字,原始粗暴,第二种,I2S库,我之前博客有讲过,数据是PCM编码。然后又去学PCM编码,一学豁然开朗,舒服,以代码为例:audio_in=I2S
我以前在Laravel4上工作过,它有一个很棒的日志查看器工具laravellogviewer查看demo我正在寻找与Rubyonrails4.2非常相似的东西,如果你们知道Rails4.2的任何好的可视化日志记录GEM,请告诉我..从代码我需要记录不同的日志级别,这个工具应该直观地组织我的日志,谢谢.. 最佳答案 这应该可以帮助您入门https://github.com/shadabahmed/logstasher如其所说Thisgemisheavilyinspiredfromlograge,butit'sfocusedonone
急促的告警铃声响彻寂静的夜晚。对运维人来说,晚间值守耗费更大的精力,往往一个简单的磁盘使用率告警通知,就不得不爬起来进行处理,毕竟告警无小事,对于小问题,运维人也不能心存侥幸心理。虽然有着值班人员和团队的支撑,但频繁的告警还是让运维人员精疲力竭,如何让系统的稳定性提高,减轻一线人员的工作量,减轻一线人员的压力?通过智能运维,实现故障自愈将成为不可避免的选择。故障自愈是提升企业网络系统可用性和降低故障处理的人力投入,实现故障自愈从"人工处理"到"无人值守"的变革。通过实时发现告警,进行预诊断分析,判断告警类型和级别,如果是一般告警,平台进行自动恢复,如果是严重复杂告警则通过告警通知、运维工单等形
数组理论基础数组是存放在连续内存空间上的相同类型数据的集合。数组下标从0开始数组内存空间的地址是连续的c++中vector和array的区别1、vector是顺序容器,其利用连续的内存空间来存储元素,但是其内存空间大小是能够改变的。2、array是顺序容器,其也是利用连续的内存空间来存储元素,但它的内存空间是固定大小的,申请之后就无法改变。3、vector的底层是array实现的二维数组二维数组在内存的空间地址是连续的704|二分查找思路1、把整个数组一分为二;2、判断目标值在左区间还是右区间,若在左区间,则修改右区间指针的位置;若在右区间,则修改新区间的左区间位置3、重复上述过程,直到lef
我想澄清这个原始post的一些事情.答案建议Ruby按以下顺序搜索常量定义:封闭范围任何外部范围(重复直到达到顶层)包含的模块父类(superclass)对象内核所以澄清一下,在第(1-6)步是为legs_in_oyster找到的常量LEGS的值?它来自父类(superclass)Animal吗?类MyAnimals的范围是否因为不被视为封闭范围而被忽略?这是由于明确的MyAnimals::Oyster类定义吗?谢谢!只是想了解。这是代码:classAnimalLEGS=4deflegs_in_animalLEGSendclassNestedAnimaldeflegs_in_neste
我正在使用谷歌可视化API创建堆积面积图。当用户将鼠标悬停在图表内的一个点上时,我希望它显示该位置点的总和,以及这些点的值。第二点,我可以通过指定选项focusTarget:'category'轻松实现。我希望在类似的外观和感觉中,在total的工具提示中多一行。我尝试通过添加一个名为Total的额外列来实现此目的,该列的值为0,但工具提示等于总和。然而,这会向图例和图表本身添加一个空行,这在视觉上并不吸引人。我觉得这应该是开箱即用的东西,但我找不到解决这个问题的方法。如果有人知道解决这个问题的好方法,请回答。提前致谢。 最佳答案
我的工作涉及大量的可视化。我一直在用D3.js和JavaScriptInfovistoolkit我最近了解到Dart如何成为开发Web应用程序的新方法。Q1。Dart是否提供任何用于可视化的库(某种级别的D3.js或JavaScriptInfovistoolkit)?Q2。如果我继续使用Dart,我可以使用D3.js吗?/JavascriptInfovistoolkit与Dart一起?编辑:我在互联网上发现wecanuseJavascriptalongwithDart.我经历了DartFAQ,但无法真正找到与可视化库或D3.js本身相关的任何内容。 最佳答案
我正在使用googlevisulaization绘制饼图。我面临的问题是,我无法捕获饼图上的点击事件。我正在这样做。functiondrawchartfromRe(){dashboard=newgoogle.visualization.Dashboard(document.getElementById('dashboard_div'));//alert("RefuelLength"+totrefuelList.length);//alert("Vehicleid:"+totrefuelList[0].vehicleId);//google.load("visualization","1