LiuY's TechBlog


  • Home

  • About

  • Archives

  • Tags

WebGL为什么快

Posted on 2018-08-11

WebGL为什么快

当我们在Canvas展示图形的过程中遇到了性能瓶颈时,总会第一个想到WebGL。我们都知道WebGL快,但是它为什么快呢。

GPU加速

可能有人会说Canvas2d也可以开启GPU加速,那么还有什么区别。区别还是有的,首先不论Canvas2d还是WebGL,最终到浏览器层时都是调用OpenGL ES的Api来进行画图,所以区别主要就在前半段了。而Canvas2d的和WebGL的主要区别在于建立模型的过程。
WebGL的绘制顶点到三角形再到着色器渲染照明的处理全都是在管道中完成的,那么意味着这个模型建立的过程全都是在GPU和显存中建立的,这个过程将硬件的优势发挥到了最大。
而Canvas呢,所有建立模型的过程都是JS代码在CPU以及内存内建立的,况且JS还有一层解释执行的引擎存在性能自然打折。

模型的管理

其实多数在Canvas2d中实现动画、图形绘制的引擎,都是自己来管理模型,根据交互或其他事件来触发重绘操作将图形绘制到Canvas中,而这个过程中,canvas被不断的绘制清空,一直持续。倘若当前模型中有成千上万个几何图形,那么每一帧都需要将这些节点进行一次重绘。
而WebGL中由于使用了管道来维护模型,那么需要渲染时,无非就是将这些图形光栅化一次而已,那么自然提高了渲染的效率。

总结

简单来说WebGl快的原因有以下两点:

  • 硬件加持
  • 模型的维护

P NP NPC NP-Hard

Posted on 2018-05-02

P NP NPC NP-Hard 问题的定义

P Problem:
如果一个问题可以找到一个能在多项式的时间里解决它的算法,那么这个问题就属于P问题。

NP Problem:
NP问题不是非P类问题。NP问题是指可以在多项式的时间里验证一个解的问题。NP问题的另一个定义是,可以在多项式的时间里猜出一个解的问题。

NP-Complete Problem:
同时满足下面两个条件的问题就是NPC问题。首先,它得是一个NP问题;然后,所有的NP问题都可以约化到它。

NP-Hard Problem:
NP-Hard问题是这样一种问题,它满足NPC问题定义的第二条但不一定要满足第一条(就是说,NP-Hard问题要比 NPC问题的范围广)。

Improving HTML5 Canvas Performance

Posted on 2018-03-21

提高Canvas渲染性能的几点总结

  • 预渲染到离屏画布

no pre-rendering:

1
2
3
4
5
// canvas, context are defined
function render() {
drawMario(context);
requestAnimationFrame(render);
}

pre-rendering:

1
2
3
4
5
6
7
8
9
10
var m_canvas = document.createElement('canvas');
m_canvas.width = 64;
m_canvas.height = 64;
var m_context = m_canvas.getContext(‘2d’);
drawMario(m_context);
function render() {
context.drawImage(m_canvas, 0, 0);
requestAnimationFrame(render);
}
  • 集中调用canvas
1
2
3
4
5
6
7
8
for (var i = 0; i < points.length - 1; i++) {
var p1 = points[i];
var p2 = points[i+1];
context.beginPath();
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
context.stroke();
}
1
2
3
4
5
6
7
8
context.beginPath();
for (var i = 0; i < points.length - 1; i++) {
var p1 = points[i];
var p2 = points[i+1];
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
}
context.stroke();
  • 避免不必要的画布状态改变
1
2
3
4
for (var i = 0; i < STRIPES; i++) {
context.fillStyle = (i % 2 ? COLOR1 : COLOR2);
context.fillRect(i * GAP, 0, GAP, 480);
}
1
2
3
4
5
6
7
8
context.fillStyle = COLOR1;
for (var i = 0; i < STRIPES/2; i++) {
context.fillRect((i*2) * GAP, 0, GAP, 480);
}
context.fillStyle = COLOR2;
for (var i = 0; i < STRIPES/2; i++) {
context.fillRect((i*2+1) * GAP, 0, GAP, 480);
}
  • 仅渲染视窗部分
1
context.fillRect(0, 0, canvas.width, canvas.height);
1
context.fillRect(last.x, last.y, last.width, last.height);
  • 将多层画布用于复杂场景
1
2
3
4
<canvas id="bg" width="640" height="480" style="position: absolute; z-index: 0">
</canvas>
<canvas id="fg" width="640" height="480" style="position: absolute; z-index: 1">
</canvas>
  • 避免使用 shadowBlur 效果
1
2
3
4
5
context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
//context.shadowBlur = 4;
context.shadowColor = 'rgba(255, 0, 0, 0.5)';
context.fillRect(20, 20, 150, 100);
  • 了解清空画布的各种方法
1
2
context.clearRect(0, 0, width, height);
canvas.width = canvas.width;
  • 避免使用浮点数坐标
1
2
3
4
rounded = (0.5 + somenum) | 0;
rounded = ~~ (0.5 + somenum);
// Finally, a left bitwise shift.
rounded = (0.5 + somenum) << 0;
  • 使用 requestAnimationFrame 优化你的动画
1
2
3
4
5
6
7
8
9
10
11
var x = 100;
var y = 100;
var lastRender = Date.now();
function render() {
var delta = Date.now() - lastRender;
x += delta;
y += delta;
context.fillRect(x, y, W, H);
requestAnimationFrame(render);
}
render();

参考链接:
https://www.html5rocks.com/en/tutorials/canvas/performance/

Debian8上搭建OpenStreetMap服务器

Posted on 2018-03-02

近期的图表需求中强调了GIS地图的优先级,因此我们需要一个稳定且低成本的GIS地图服务器,对比高德和百度的商业合作解决方案后,我们决定使用开源地图OpenStreetMap。

需要强调的是OpenStreetMap的地图数据是免费开源的,但它的地图服务不是,因此如果想使用OpenStreetMap的地图服务,有两个选择:选择付费的服务提供商(例如mapbox)或自己搭建地图服务器,考虑到服务器响应速度和地图的功能需求,最终决定我们自己动手搭建OpenStreetMap瓦片服务器。

本文中使用的服务器操作系统为Debian8.8,部署过程需要root权限。

创建系统用户

1
2
sudo -iu root
adduser youdata

PostgreSQL & Postgis

PostgreSQL用来存储地图数据,PostGIS是其地理空间插件,相比MySQL,PostgreSQL拥有更好的几何类型支持和空间数据库扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# root用户安装PostgreSQL和插件
sudo -iu root
apt install postgresql-9.4-postgis-2.1 postgresql-contrib-9.4
# 切换到postgres用户编辑数据库
sudo -iu postgres
cd ~
# 创建数据库用户
createuser youdata
# 创建地图数据库
createdb -E UTF8 -O youdata gis
# 为gis数据库增加hstore和postgis扩展
psql
\c gis
CREATE EXTENSION postgis;
CREATE EXTENSION hstore;
# Should answer CREATE EXTENSION
exit

osm2pgsql

osm2pgsql的作用是将OSM数据转换并导入PostGIS里的工具

1
2
sudo -iu root
apt install osm2pgsql

Stylesheet

获取样式表

1
2
3
4
su youdata
cd ~
wget https://github.com/gravitystorm/openstreetmap-carto/archive/v2.29.1.tar.gz
tar -xzf v2.29.1.tar.gz

OSM Data

获取OSM的地图数据

http://download.geofabrik.de/index.html

http://planet.openstreetmap.org/pbf/

1
2
3
su youdata
cd ~
http://download.geofabrik.de/asia/china-latest.osm.pbf

导入地图数据

1
2
3
su youdata
cd ~
osm2pgsql --slim -d gis -C 6000 --hstore -S openstreetmap-carto-2.29.1/openstreetmap-carto.style china-latest.osm.pbf

mod_tile

mod_tile是OSM的地图瓦片服务,没有Debian8的适配版本,需要从源码进行编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
su youdata
cd ~
wget https://github.com/openstreetmap/mod_tile/archive/6c2cb243e4c8b047950ab8062cd66245f20a5d2f.tar.gz -O mod_tile.tar.gz
tar -xzf mod_tile.tar.gz
mv mod_tile-6c2cb243e4c8b047950ab8062cd66245f20a5d2f mod_tile
sudo -iu root
apt install autoconf libtool libmapnik-dev apache2-dev
exit
cd mod_tile
./autogen.sh
./configure
make
sudo -iu root
cd /home/youdata/mod_tile
make install
make install-mod_tile
exit

Generate Mapnik Style

服务端地图渲染

1
2
3
4
5
6
7
8
9
10
11
12
sudo -iu root
apt install curl unzip gdal-bin mapnik-utils node-carto
# 在线获取shapefiles
su youdata
cd ~/openstreetmap-carto-2.29.1/
./get-shapefiles.sh
# 修改数据库名称 本例中不用修改
# sed -i 's/"dbname": "gis"/"dbname": "mygis"/' project.mml
carto project.mml > style.xml

Font

安装地图渲染所需要的中文字体

1
2
sudo -iu root
apt-get install unifont

renderd

配置服务端地图渲染

修改/usr/local/etc/renderd.conf

1
2
3
4
5
6
7
8
9
[mapnik]
plugins_dir=/usr/lib/mapnik/2.2/input/
font_dir=/usr/share/fonts/truetype/unifont
font_dir_recurse=1
[default]
URI=/osm/
XML=/home/youdata/openstreetmap-carto-2.29.1/style.xml
HOST=localhost

修改/home/youdata/mod_tile/debian/renderd.init

1
2
3
DAEMON=/usr/local/bin/$NAME
DAEMON_ARGS=" -c /usr/local/etc/renderd.conf"
RUNASUSER=youdata
1
2
3
4
5
6
7
8
9
10
sudo -iu youdata
cd /home/youdata
cp mod_tile/debian/renderd.init /etc/init.d/renderd
chmod a+x /etc/init.d/renderd
mkdir -p /var/lib/mod_tile
chown youdata /var/lib/mod_tile
systemctl daemon-reload
service renderd start

Apache2

文件服务器

1
apt install apache2

编辑/etc/apache2/sites-enabled/000-default.conf

1
2
3
4
5
6
LoadTileConfigFile /usr/local/etc/renderd.conf
ModTileRenderdSocketName /var/run/renderd/renderd.sock
# Timeout before giving up for a tile to be rendered
ModTileRequestTimeout 0
# Timeout before giving up for a tile to be rendered that is otherwise missing
ModTileMissingRequestTimeout 30

如果需要开启跨域访问的话

1
2
sudo a2enmod headers
sudo vi /etc/apache2/sites-available/000-default.conf

编辑添加:

LoadModule headers_module modules/mod_headers.so
Header set Access-Control-Allow-Origin *

1
2
3
4
5
6
7
8
<VirtualHost *:80>
LoadModule headers_module modules/mod_headers.so
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
Header set Access-Control-Allow-Origin *
</VirtualHost>

启动服务

1
2
3
4
echo "LoadModule tile_module /usr/lib/apache2/modules/mod_tile.so" > /etc/apache2/mods-available/tile.load
ln -s /etc/apache2/mods-available/tile.load /etc/apache2/mods-enabled/
service apache2 restart

测试服务是否正常

访问http://localhost/osm/0/0/0.png

通过以下命令输出日志

1
sudo tail -f /var/log/syslog | grep " TILE "

使用openstreetmap重定向测试地图服务

安装chrome重定向插件”Switcheroo Redirector”

https://chrome.google.com/webstore/detail/switcheroo-redirector/cnmciclhnghalnpfhhleggldniplelbg?hl=en

添加以下重定向:

  • https://tile-a.openstreetmap.fr/hot/ => http://10.165.124.194/osm/
  • https://tile-a.openstreetmap.fr/hot/ => http://10.165.124.194/osm/
  • https://tile-a.openstreetmap.fr/hot/ => http://10.165.124.194/osm/

访问http://www.openstreetmap.org/

OSM中选择”Humanitarian”图层

参考资料:

https://wiki.debian.org/OSM/tileserver/jessie#Install_mod_tile

https://switch2osm.org/manually-building-a-tile-server-16-04-2-lts/

JavaScript memory leak

Posted on 2018-02-05

JavaScript is secretly a functional programming language, and its functions are closures: function objects get access to variables defined in their enclosing scope, even when that scope is finished.

Local variables which are captured by a closure are garbage collected once the function they are defined in has finished and all functions defined inside their scope are themselves GCed.

1
2
3
4
5
6
7
8
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
theThing = {
longStr: new Array(1000000).join('*')
};
};
setInterval(replaceThing, 1000);

Every second, we’ll execute the replaceThing function. It replaces theThing with a new object containing a newly allocated giant string, saving the original value of theThing in the local variable originalThing. After it returns, the old value of theThing can be garbage collected, including the long string inside it, since nothing remains that refers to it. So the memory used by this code is roughly constant.

But what if we had a closure that outlasted replaceThing?

1
2
3
4
5
6
7
8
9
10
11
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log(someMessage);
}
};
};
setInterval(replaceThing, 1000);

Fortunately, modern JavaScript implementations (including, say, the current V8 in Chrome and Node) are smart enough to notice that originalThing isn’t actaully used in the closure someMethod, so it’s not put into someMethod’s lexical environment, and it’s OK to GC the previous theThing when replaceThing finishes.

(But wait, you ask! What if somebody had earlier run console.log = eval, and so the seemingly innocuous line console.log(someMessage) was actually evaling some code that refers to originalThing? Well, the JavaScript standard is one step ahead of you. If you useeval in a sneaky fashion like this (in any way other than by just calling it eval), it’s called an “indirect eval”, and it doesn’t actually get to access the lexical environment! If, on the other hand, someMethod did contain a direct call to eval with that name, it could in fact access originalThing, and the JavaScript environment is forbidden from keeping originalThingout of the lexical environment and this would end up leaking.)

Well, great! JavaScript protects us from memory leaks, right? Well, let’s try one more version, combining the first two examples.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var theThing = null;
var replaceThing = function(){
var originalThing = theThing;
var unused = function(){
if(originalThing)
console.log(“hi”);
};
theThing = {
longStr:new Array(1000000).join('*'),
someMethod:function(){
console.log(someMessage);
}
};
};
setInterval(replaceThing,1000);

But isn’t this just the same situation as before? originalThing is only referenced in the main body of replaceThing, and in unused. unused itself (which we never even run!) gets cleaned up once replaceThing ends… the only thing from replaceThing that escapes is the second closure, someMethod. And someMethod doesn’t refer to originalString at all!

So even though there’s no way for any code to ever refer to originalThing again, it never gets garbage collected! Why? Well, the typical way that closures are implemented is that every function object has a link to a dictionary-style object representing its lexical scope. If both functions defined inside replaceThing actually used originalThing, it would be important that they both get the same object, even if originalThing gets assigned to over and over, so both functions share the same lexical environment. Now, Chrome’s V8 JavaScript engine is apparently smart enough to keep variables out of the lexical environment if they aren’t used byany closures: that’s why the first example doesn’t leak.

But as soon as a variable is used by any closure, it ends up in the lexical environment shared byall closures in that scope. And that can lead to memory leaks.

Just add originalThing = null to the end of replaceThing. That way, even though the name originalThing is still in the lexical environment of someMethod, there won’t be a link to the big old value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
// Define a closure that references originalThing but doesn't ever
// actually get called. But because this closure exists,
// originalThing will be in the lexical environment for all
// closures defined in replaceThing, instead of being optimized
// out of it. If you remove this function, there is no leak.
var unused = function () {
if (originalThing)
console.log("hi");
};
theThing = {
longStr: new Array(1000000).join('*'),
// While originalThing is theoretically accessible by this
// function, it obviously doesn't use it. But because
// originalThing is part of the lexical environment, someMethod
// will hold a reference to originalThing, and so even though we
// are replacing theThing with something that has no effective
// way to reference the old value of theThing, the old value
// will never get cleaned up!
someMethod: function () {}
};
// If you add `originalThing = null` here, there is no leak.
};
setInterval(replaceThing, 1000);

So in summary: If you have a large object that is used by some closures, but not by any closures that you need to keep using, just make sure that the local variable no longer points to it once you’re done with it.

https://blog.meteor.com/an-interesting-kind-of-javascript-memory-leak-8b47d2e7f156

新媒体时代的数据可视化

Posted on 2017-10-09

新媒体时代的数据可视化 – 浙大陈为教授分享

数据可视化是什么


人眼多线程并行传感器,信息通量很大,一图胜千言

视觉通道

  • 颜色、大小、形状、宽高、纹理、位置 等

可视化的定义


Computer-based visualization systems provide visual representations of datasts intended to help people carrt out some task more effectively.

几何形态 <==> 数据模型

可视化作用

  • 记录信息
  • 传播信息
  • 分析推理

记录信息

拿破仑东征


https://www.edwardtufte.com/tufte/minard

用图讲故事

NASA 洋流向量场

https://www.youtube.com/watch?v=CCmTY0PKGDs

传播信息

让子弹飞

分析推理

十九世纪伦敦城的死亡地图


http://news.ifeng.com/a/20171006/52364520_0.shtml

水坝分析

数字模拟参数与结果之间的关系

可视化目标

  • 信:忠实原文
  • 达:通顺,流畅
  • 雅:优雅,美好

新媒体时代的数据可视化

  • 雅虎股票走势
  • 东方航空使用tableau
  • 棱镜门事件分析网络数据

1987 科学可视化 科研人员 => 1990 信息可视化 普通用户 => 2004 可视分析 分析师

可视化 ={ 图形 + 数据 + 设计 + 交互

FPS游戏可视化分析:


http://blog.counter-strike.net/science/maps.html

雅虎电子邮件

路透社新闻

微博转发

微软学术搜索 可视化研究

CNN #COP17 ECOSPHERE Project

WWW.CNN-ECOSPHERE.COM/COP17

浙江大学的数据可视化研究

  • 信息监控
  • 计算仿真
  • 商业智能
  • 信息安全
  • 可视化组件库
  • 智慧地球

Clojure Development Environment

Posted on 2017-09-01

Pre-condition

  • MAC OS
  • JDK 1.8
  • homebrew
  • IntelliJ IDEA

Setup clojure

1
2
3
4
5
brew update
brew install leiningen
brew install clojurescript

ClojureScript REPL

1
lein repl

exit repl: control + d

setup clojure IDEA plugin

  • Leiningen
  • Cursive

MongoDB学习笔记

Posted on 2017-08-30

MongoDB

基于分布式文件存储的数据库

MongoDB中数据的基本单元称为文档(Document),它是MongoDB的核心概念,由多个键极其关联的值有序的放置在一起组成,数据库中它对应于关系型数据库的行。

数据在MongoDB中以BSON(Binary-JSON)文档的格式存储在磁盘上。

基本操作

增

1
2
3
4
db.collection.insert(document);
db.collection.insert([doc1, doc2, ...]);
db.person.insert({name:"Liuy", age: 27});

查

1
2
3
4
5
show collections
db.collection.find();
db.collection.find(criteria,projection);
db.person.find({age:{$gt:18}},{name:1}).sort({age:1}).limit(5);
  • criteria – 查询条件,文档类型,可选。
  • projection– 返回的字段,文档类型,可选,若需返回所有字段,则忽略此参数。

改

1
2
3
4
5
db.collection.update(query,update,{upsert:boolean,multi:boolean});
db.collection.save(document);
db.person.update({age:{$gt:18}},{$set:{status:"A"}},{multi:true});
db.person.save({name:"Tony",age:12,gender:"man"});

save命令可以更新或插入一个新文档

  • query:查询条件,文档,和find中的查询条件写法一致。
  • update:修改内容,文档。
  • upsert(可选):如果值为true,那么当集合中没有匹配文档时,创建文档。默认false。
  • multi(可选):如果值为true,那么将更新全部符合条件的文档,否则仅更新一个文档,默认false。

删

1
2
3
db.collection.remove(query,justOne);
db.person.remove({status:"D"});
  • query:BSON类型,删除文档的条件。
  • justOne:布尔类型,true:只删除一个文档,false:默认值,删除所有符合条件的文档。

字段查询

相等条件

1
db.person.find({status:"A"});

比较条件

1
db.person.find({age:{$gt:40}});

数组条件

当字段包含数组,可以进行数组完全匹配或匹配特定的值

1
2
3
4
5
6
7
8
// 数组完全匹配
db.inventory.find( { tags: [ 'fruit', 'food', 'citrus' ] } );
// 单个元素匹配
db.inventory.find( { tags: 'fruit' } );
// 特定元素匹配 特定元素匹配需要"字段.索引"形式传入参数。
db.inventory.find( { 'tags.0' : 'fruit' } );

子文档条件

1
db.mycol.find({"access.level":5});

复合查询 AND/OR

1
2
3
4
5
db.collection.find({$and:[doc1,doc2,...]})
db.collection.find({$or:[doc1,doc2,...]})
db.person.find({$and:[{age:{$gt:30}},{name:"Lucy"}]});
db.person.find({$or:[{status:"A"},{age:30}]});

游标与结果集

cursor游标

find命令并不直接返回结果,而是返回一个结果集的迭代器,即游标。

可以使用next方法来遍历游标

1
2
3
4
5
6
var myCursor = db.inventory.find( { type: "food" } );
var myDocument = myCursor.hasNext() ? myCursor.next() : null;
if (myDocument) {
var myItem = myDocument.item;
print(tojson(myItem));
}

使用forEach方法也能实现游标的遍历

1
2
var myCursor = db.inventory.find( { type: "food" } );
myCursor.forEach(printjson);

结果集

限制条件

1
2
db.collection.find().limit(Num)
db.person.find({},{name:1})

Gopher杭州Meetup

Posted on 2017-08-05

Gopher杭州Meetup

可视化学习 Go 并发编程

黄庆兵:网易

围绕Go并发编程,结合gotrace工具,让你眼见为实学习Go并发编程

GoTrace + three.js

算法可视化

基于Go的大数据平台

陈超:七牛

大数据一直是我们在谈论的话题,不管你数据到底有多大?陈超带来全新的大数据处理思路。

七牛云广告

分布式计算调度算法: 根据CPU、内存、网络、任务分布度等系统资源占用情况进行打分

Kubernetes语境下的基于Golang的编程范式

张磊:Hyper项目成员,Kubernetes项目官方Project Manager和Feature Maintainer

介绍Kubernetes项目中面向Etcd的对象控制器、工作队列、自定义资源、容器运行时接口等一系列基于Golang的编程范式,同时讲解开发者如何使用这些范式来“无损”地扩展Kubernetes核心能力的最佳实践。

  • Kubernetes
    Kubernetes作为Docker生态圈中重要一员,是Google多年大规模容器管理技术的开源版本,是产线实践经验的最佳表现[G1] 。
    面向 API ”OO“ Programming

  • etcd
    可用强一致性的服务发现存储仓库

  • gRPC
    是一个高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发语言。

  • RPC
    远程过程调用(英语:Remote Procedure Call,縮寫為RPC)是一个计算机通信协议。 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。

基于Mesos的容器调度框架

黄励博:又拍云

为了更好地完成云处理服务的负载均衡,零停机升级,自定义策略调度等功能,又拍云用Go实现了自己的容器调度框架upone,管理长期服务和定时任务。

  • Raft
    Raft 是一种用来管理日志复制的一致性算法

报警使用slack

Go使用gRPC+http打造高性能微服务

谢孟军:GoCN

GRPC已成为互联网最佳的通讯标准,如何打造一个高性能的微服务?

微服务 MicroServices

小、独立服务
微服务可以按照功能来划分,或披萨理论,开发到部署3-4个人

gRPC

基于HTTP/2 POST protobuf

JSON protobuf
plaintext binary
human readable machine readable
repetitive compressed
fast (de)serialization faster (de) serialization
Everything supports it limited support
HTTP/1.1 HTTP/2
plaintext binary
no pipelining native pipelining
new connection per request persistent TCP
repetitive compressed
polling streaming
Non-secure by default TLS by default
Everything supports it limited support
  • REST
    REpresentational State Transfer,全称是 Resource Representational State Transfer:通俗来讲就是:资源在网络中以某种表现形式进行状态转移。
    URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作。

如何使用gRPC+http打造微服务

为什么GO适合做微服务

  • 小

NEV与其他图表引擎的横向对比

Posted on 2017-08-04

NEV与其他图表引擎的横向对比

对比的其他图表库包括vega、HighCharts、 ECharts以及G2

图表类型

  • 柱状图
—- vega HighCharts Echarts G2 NEV
堆叠柱状图 ✅ ✅ ✅ ✅ ✅
并列柱状图 ✅ ✅ ✅ ✅ ✅
百分比堆叠柱状图 ✅ ✅ ❌ ✅ ✅表计算
并列堆叠柱状图 ✅ ✅ ✅ ✅ ✅
转置柱状图 ✅ ✅ ✅ ✅ ✅
  • 折线图、区域图
—- vega HighCharts Echarts G2 NEV
普通折线图 ✅ ✅ ✅ ✅ ✅
平滑折线图 ✅ ✅ ✅ ✅ ✅
转置折线图 ✅ ✅ ✅ ✅ ✅
区域图 ✅ ✅ ✅ ✅ ✅
百分比堆叠区域图 ✅ ✅ ❌ ✅ ✅表计算
范围区域图 ✅ ✅ ❌ ✅ ❌
转置区域图 ✅ ✅ ✅ ✅ ✅
  • 饼图
—- vega HighCharts Echarts G2 NEV
饼图 ✅ ✅ ✅ ✅ ✅
玫瑰图 ✅ ✅ ✅ ✅ ✅
环形图 ✅ ✅ ✅ ✅ ✅
环形玫瑰图 ❌ ✅ ✅ ✅ ✅
笛卡尔饼图 ❌ ❌ ❌ ❌ ✅
  • 散点图、气泡图
—- vega HighCharts Echarts G2 NEV
散点图 ✅ ✅ ✅ ✅ ✅
文本图 ❌ ❌ ❌ ❌ ✅
气泡图 ✅ ✅ ✅ ✅ ✅
  • 地图
—- vega HighCharts Echarts G2 NEV
地图散点图 ✅ ✅ ✅ ✅ ✅
填充地图 ✅ ✅ ✅ ✅ ✅
迁徙地图 ❌ ❌ ✅ ❌ ✅nev-lite
热力地图 ❌ ❌ ✅ ✅ ✅nev-lite
  • 组合图
—- vega HighCharts Echarts G2 NEV
折现柱状图 ✅ ✅ ✅ ✅ ✅
散点柱状图 ✅ ✅ ✅ ✅ ✅
地图饼图 ✅ ❌ ❌ ❌ ✅
  • 高级图表
—- vega HighCharts Echarts G2 NEV
子弹图 ❌ ❌ ❌ ❌ ✅
仪表图 ❌ ✅ ✅ ✅ ✅
树图 ✅ ✅ ✅ ✅ ✅
词云 ✅ ❌ ❌ ❌ ✅
漏斗图 ❌ ✅ ✅ ✅ ✅
热力图 ❌ ✅ ✅ ✅ ✅
3D图 ❌ ✅ ✅ ❌ ❌
关系图 ✅ ❌ ✅ ✅ ❌
雷达图 ❌ ✅ ✅ ✅ ✅
箱线图 ✅ ✅ ✅ ✅ ✅
平行坐标系 ✅ ❌ ✅ ❌ ❌
桑基图 ❌ ❌ ✅ ✅ ✅
瀑布图 ❌ ✅ ❌ ❌ ✅
甘特图 ❌ ❌ ❌ ❌ ✅
  • 图表高级功能
—- vega HighCharts Echarts G2 NEV
对数轴 ✅ ✅ ✅ ✅ ✅
时间轴 ✅ ✅ ✅ ✅ ✅
双轴 ✅ ✅ ✅ ✅ ✅
透视 ✅ ❌ ❌ ✅ ✅
参考线 ❌ ✅ ✅ ❌ ✅
主题样式 ❌ ✅ ✅ ❌ ✅

移动端定制版支持

图表库 详细
vega ❌
HighCharts ❌
Echarts ❌
G2 ✅ 有移动端版,但该仅支持部分图表且无交互
NEV ✅ 几乎所有图表均提供移动端定制版,且针对移动端提供定制化交互

交互丰富程度与定制性

图表库 交互丰富程度 移动端交互 可定制性
vega ⭐️⭐️ ❌ ✅
HighCharts ⭐️⭐️ ❌ ❌
Echarts ⭐️⭐️⭐️⭐️⭐️ ❌ ❌
G2 ⭐️⭐️⭐️⭐️ ❌ ❌
NEV ⭐️⭐️⭐️⭐️ ✅ ✅

图表扩展组合能力

图表库 详细
vega ✅
HighCharts ❌
Echarts ❌
G2 ✅
NEV ✅

接口功能与易用性

图表库 接口功能 易用性
vega ⭐️⭐️⭐️⭐️ ⭐️⭐️
HighCharts ⭐️⭐️ ⭐️⭐️⭐️⭐️
Echarts ⭐️⭐️ ⭐️⭐️⭐️⭐️
G2 ⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️⭐️
NEV ⭐️⭐️⭐️⭐️⭐️ ⭐️⭐️

总结

  • vega: 绘图功能强大,遵循图形语法,因此带来了强大的图表组合能力,但由于是非商业化的开源项目,因此在视觉、交互等方面有所欠缺,而且接口复杂,学习曲线略微陡峭。

  • HighCharts: 图表类型丰富,不支持图表元素组合扩展,带有商业性质的项目,商业用途需购买授权,难以进行二次开发,对于不支持的图表难以扩展。

  • Echarts: 图表类型丰富,提供良好的视觉和交互体验,不遵循图形语法,不能进行图表组合扩展,免费开源项目,可以用于商业用途,难以进行二次开发,对于不支持的图表难以扩展。

  • G2: 遵循图形语法,接口使用方便,非开源项目,各方面能力较为均衡。

  • NEV: 遵循图形语法,具有图表组合扩展能力,支持用户进行高度定制化的配置需求,提供良好的视觉和交互体验,提供移动端定制版,支持交互定制,高度支持图表透视功能,缺点是用户接口使用略有不便。

12
LiuY

LiuY

14 posts
11 tags
© 2018 LiuY
Powered by Hexo
Theme - NexT.Muse