博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
空间数据可视化之ArcLayer详解
阅读量:5350 次
发布时间:2019-06-15

本文共 8047 字,大约阅读时间需要 26 分钟。

deck-overlay中

首先使用d3中的scaleQuantile将数据进行分类,scaleQuantile方法是d3中的一种数据分类方法(https://www.cnblogs.com/kidsitcn/p/7182274.html)

https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/arc/counties.json

 

 

_getArcs({data, selectedFeature}) {    if (!data || !selectedFeature) {      return null;    }    const {flows, centroid} = selectedFeature.properties;    const arcs = Object.keys(flows).map(toId => {      const f = data[toId];      return {        source: centroid,        target: f.properties.centroid,        value: flows[toId]      };    });    const scale = scaleQuantile()      .domain(arcs.map(a => Math.abs(a.value)))      .range(inFlowColors.map((c, i) => i));    arcs.forEach(a => {      a.gain = Math.sign(a.value);      a.quantile = scale(Math.abs(a.value));    });    return arcs;  }

scaleQuantile是一种将连续的值转化成离散的方法,最终离散成这几种颜色分类

arc-layer中

这里还是使用了实例化的方法,先添加一堆实例化变量:

initializeState() {    const attributeManager = this.getAttributeManager();    /* eslint-disable max-len */    attributeManager.addInstanced({      instancePositions: {        size: 4,        transition: true,        accessor: ['getSourcePosition', 'getTargetPosition'],        update: this.calculateInstancePositions      },      instanceSourceColors: {        size: 4,        type: GL.UNSIGNED_BYTE,        transition: true,        accessor: 'getSourceColor',        update: this.calculateInstanceSourceColors      },      instanceTargetColors: {        size: 4,        type: GL.UNSIGNED_BYTE,        transition: true,        accessor: 'getTargetColor',        update: this.calculateInstanceTargetColors      }    });    /* eslint-enable max-len */  }

然后是制作图形,这里使用50个点来模拟一条抛物线的效果

_getModel(gl) {    let positions = [];    const NUM_SEGMENTS = 50; // 利用50个点来模拟曲线    /*     *  (0, -1)-------------_(1, -1)     *       |          _,-"  |     *       o      _,-"      o     *       |  _,-"          |     *   (0, 1)"-------------(1, 1)     */    for (let i = 0; i < NUM_SEGMENTS; i++) { // 使用三角带的方式来绘制三角形,同时这里的-1和1也是为了在绘制宽度的时候确定法向量的偏移      positions = positions.concat([i, -1, 0, i, 1, 0]);    }    const model = new Model(      gl,      Object.assign({}, this.getShaders(), {        id: this.props.id,        geometry: new Geometry({          drawMode: GL.TRIANGLE_STRIP,          attributes: {            positions: new Float32Array(positions)          }        }),        isInstanced: true,        shaderCache: this.context.shaderCache // 缓存着色器,我怀疑自己写的hexagon偏慢也跟这个有关系      })// 绘制物体,这里是5.x的版本在新的版本中还要设定instanceCount参数,来控制绘制实例的数量    );    model.setUniforms({numSegments: NUM_SEGMENTS});    return model;  }

下面是计算一些实例变量,根据data的数量来控制,但是luma好像会默认给实例变量的数组分配大小,实际的value中有一些多余的空间,如果数据量小的话,可能绘制不出来;比如:data有22条线,按照如下计算,instancePositions可用的value就只有88个元素。

calculateInstancePositions(attribute) {    const {data, getSourcePosition, getTargetPosition} = this.props;    const {value, size} = attribute;    let i = 0;    for (const object of data) {      const sourcePosition = getSourcePosition(object);      const targetPosition = getTargetPosition(object);      value[i + 0] = sourcePosition[0];      value[i + 1] = sourcePosition[1];      value[i + 2] = targetPosition[0];      value[i + 3] = targetPosition[1];      i += size;    }  }  calculateInstancePositions64Low(attribute) {    const {data, getSourcePosition, getTargetPosition} = this.props;    const {value, size} = attribute;    let i = 0;    for (const object of data) {      const sourcePosition = getSourcePosition(object);      const targetPosition = getTargetPosition(object);      value[i + 0] = fp64LowPart(sourcePosition[0]);      value[i + 1] = fp64LowPart(sourcePosition[1]);      value[i + 2] = fp64LowPart(targetPosition[0]);      value[i + 3] = fp64LowPart(targetPosition[1]);      i += size;    }  }  calculateInstanceSourceColors(attribute) {    const {data, getSourceColor} = this.props;    const {value, size} = attribute;    let i = 0;    for (const object of data) {      const color = getSourceColor(object);      value[i + 0] = color[0];      value[i + 1] = color[1];      value[i + 2] = color[2];      value[i + 3] = isNaN(color[3]) ? 255 : color[3];      i += size;    }  }  calculateInstanceTargetColors(attribute) {    const {data, getTargetColor} = this.props;    const {value, size} = attribute;    let i = 0;    for (const object of data) {      const color = getTargetColor(object);      value[i + 0] = color[0];      value[i + 1] = color[1];      value[i + 2] = color[2];      value[i + 3] = isNaN(color[3]) ? 255 : color[3];      i += size;    }  }

着色器代码

 

#define SHADER_NAME arc-layer-vertex-shaderattribute vec3 positions; // 几何图形的坐标,同时这里面也编码了一些信息,x代表线段索引,y可以代表偏移方向// 本次可用的一些实例变量attribute vec4 instanceSourceColors;// 起点的颜色attribute vec4 instanceTargetColors; // 终点的颜色attribute vec4 instancePositions; // 前两个值记录了起点经纬度,后两个值记录了终点经纬度attribute vec3 instancePickingColors;uniform float numSegments; // 抛物线的线段数量uniform float strokeWidth; // 线宽度uniform float opacity;varying vec4 vColor;// source和target是在3d空间中的单位,ratio代表本此线段在总线段数目的比值范围在0~1,返回值时抛物线高度的平方// 这里的方式决定高度单位与source/target的单位保持一致float paraboloid(vec2 source, vec2 target, float ratio) {  vec2 x = mix(source, target, ratio); // 获取该线段节点对应的直线位置  vec2 center = mix(source, target, 0.5);// 取中心点,充分利用glsl内建函数,提升性能  // 抛物线的公式应该是y * y = (source - center)^2 - (x - center)^2;  float dSourceCenter = distance(source, center);  float dXCenter = distance(x, center);  return (dSourceCenter + dXCenter) * (dSourceCenter - dXCenter);}// 在屏幕空间中计算偏移值,最后在反算到裁切空间,也就是ndc空间// offset_direction在position的y坐标中记录// offset vector by strokeWidth pixels// offset_direction is -1 (left) or 1 (right)vec2 getExtrusionOffset(vec2 line_clipspace, float offset_direction) {  // normalized direction of the line  // ndc空间中的坐标乘以屏幕宽高像素,转换成2维屏幕像素;然后归一化成单位向量  vec2 dir_screenspace = normalize(line_clipspace * project_uViewportSize);  // rotate by 90 degrees  dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x); // 求法线向量  // 法向量乘以偏移方向乘以宽度一半获取在屏幕空间中的偏移值  vec2 offset_screenspace = dir_screenspace * offset_direction * strokeWidth / 2.0;  // 将屏幕坐标反算到ndc空间  vec2 offset_clipspace = project_pixel_to_clipspace(offset_screenspace).xy;  return offset_clipspace; // 返回ndc空间的偏移量}float getSegmentRatio(float index) { // 返回线段索引在总线段数目中的比值,转换成0~1之间  return smoothstep(0.0, 1.0, index / (numSegments - 1.0));}vec3 getPos(vec2 source, vec2 target, float segmentRatio) { // 获取线段节点在三维空间中的位置  float vertex_height = paraboloid(source, target, segmentRatio); // 获取高度信息  return vec3(    mix(source, target, segmentRatio), // 获取节点的x/y坐标    sqrt(max(0.0, vertex_height))// 获取节点的高度坐标  );}void main(void) {  // 将insance中编码的起终点的经纬度分别转换成瓦片像素单位  vec2 source = project_position(instancePositions.xy);  vec2 target = project_position(instancePositions.zw);  float segmentIndex = positions.x;// 节点的线段索引  float segmentRatio = getSegmentRatio(segmentIndex);  // if it's the first point, use next - current as direction  // otherwise use current - prev  // 这里处理方式比较巧妙,充分利用内建函数优势;  // step(edge, x) 作用如: x>=edge ? 1.0 : 0.0  // 所以上面英文注释所说,如果是起点就使用next-curr,其他的都是用curr - prev  //float indexDir = mix(-1.0, 1.0, step(segmentIndex, 0.0));  float indexDir = mix(-1.0, 1.0, (segmentIndex <= 0.0 ? 1.0 : 0.0));  // 根据indexDir获取下一段或者上一个线段节点的比值  float nextSegmentRatio = getSegmentRatio(segmentIndex + indexDir);  // 获取两个节点的3维世界坐标并转化成ndc坐标  vec3 currPos = getPos(source, target, segmentRatio);  vec3 nextPos = getPos(source, target, nextSegmentRatio);  vec4 curr = project_to_clipspace(vec4(currPos, 1.0));  vec4 next = project_to_clipspace(vec4(nextPos, 1.0));  // extrude  // 进行线宽拉伸,获取法线方向的偏移  vec2 offset = getExtrusionOffset((next.xy - curr.xy) * indexDir, positions.y);  gl_Position = curr + vec4(offset, 0.0, 0.0); // 获取最终节点的ndc位置  // 根据线段节点位置计算颜色插值  vec4 color = mix(instanceSourceColors, instanceTargetColors, segmentRatio) / 255.;  vColor = vec4(color.rgb, color.a * opacity);// 获取最终颜色  // Set color to be rendered to picking fbo (also used to check for selection highlight).  picking_setPickingColor(instancePickingColors);}

 

转载于:https://www.cnblogs.com/dojo-lzz/p/10129152.html

你可能感兴趣的文章
8-EasyNetQ之Send & Receive
查看>>
Android反编译教程
查看>>
List<string> 去重复 并且出现次数最多的排前面
查看>>
js日志管理-log4javascript学习小结
查看>>
Android之布局androidmanifest.xml 资源清单 概述
查看>>
How to Find Research Problems
查看>>
Linux用户管理
查看>>
数据库第1,2,3范式学习
查看>>
《Linux内核设计与实现》第四章学习笔记
查看>>
使用iperf测试网络性能
查看>>
图片的显示隐藏(两张图片,默认的时候显示第一张,点击的时候显示另一张)...
查看>>
Docker 安装MySQL5.7(三)
查看>>
python 模块 来了 (调包侠 修炼手册一)
查看>>
关于CSS的使用方式
查看>>
分析语句执行步骤并对排出耗时比较多的语句
查看>>
原生JS轮播-各种效果的极简实现
查看>>
计数器方法使用?
查看>>
带你全面了解高级 Java 面试中需要掌握的 JVM 知识点
查看>>
sonar结合jenkins
查看>>
解决VS+QT无法生成moc文件的问题
查看>>