od找数据 遇到dll_地理数据可视化

导读

阅读完此文,你会了解:1、常见的地理数据可视化图层及分类;2、GeoJSON编码格式;3、点的图层如何实现
4、OD弧线图层如何实现
5、热力图层如何实现

数据可视化图层

底图 vs 数据可视化图层

通过之前的文章,即GIS坐标体系、相机控制、数据源的金字塔构成以及二进制的解析,可以得到一个基础的矢量瓦片数据信息,包括:道路、水域、功能面、建筑、POI等信息,辅以前几篇文章(查看请访问本文末尾链接)提到的建筑的渲染、文字的渲染,再加上道路的渲染(有机会再讲)、功能面、水,基本可以构成一个较为完整的地理信息地图引擎。

88310935ef1e569e2ead9dde5eb0e7df.png

地理信息地图

瓦片或矢量瓦片作为地图的底图,通常只用做地理信息的表达,如果想在地理信息底图的基础上绘制,就需要疯狂输出可视化图层,这种图层通常用来快速制作出如散点、轨迹、区面、热力图等地理位置相关的可视化作品。

       122c0693131c18256191b9e71b909d4a.png

图层示意

地图底图和可视化图层的关系,就好比 Mapbox和Deck.gl,高德和Loca.js。

数据可视化图层分类

经过与Loca、Deck.gl的分析和收集对比,我们根据数据源类型(点、线、面)以及可视化后的呈现方式,将图层大致分为了5类:

  • 点类型数据图层

       d0d44f7873cd6d24c42a05085577aaff.png    

  • 线类型数据图层

       e15802fa196568ac274ffc6fbd254dad.png      

  • 面类型数据图层

       e0a83776684b2a83432c11959e76415f.png      

  • 热力类型图层

       fb5f6f65ed64686b6f5f7f07d94bb542.png      

  • 其他数据模型图层

       9f125dac162039abbdadcb59237018f0.png

样式和数据定义

数据格式

数据结构的定义我们采用了GeoJSON的编码格式,GeoJSON是一种对各种地理数据结构进行编码的格式,每一条数据,都叫做一个特征(Feature),特征的几何类型包括:

1、点:Point、MultiPoint

2、线:LineString、MultiLineString

3、面:Polygon、MultiPolygon

       551e2f6e5fd90764258b21175a484849.png

GeoJSON特征

geometry属性用来描述几何属性,properties用来描述其他属性,例如:投影、bbox等。

更多规则可以参考GeoJSON规范文档:https://geojson.org/geojson-spec.html

样式格式

在样式格式上,我们借鉴了OpenLayers的样式规则,使用"image"字段来描述点的样式信息,"fill"字段描述(面)填充色样式,"stroke"字段来描述(面、线)的描边样式。

051b4ac9277788fb87b4c184781d5603.png

样式结构

样式合并策略

图层中特征样式的修改和设置,有时候会需要重新生成顶点和面,例如将一个高度为0的行政区设置为有高度值的行政区多边形。如果频繁设置样式,会造成CPU的消耗。为了规避这类问题,我们对样式设置的操作设置了优先合并,延迟更新的策略。即在同一帧内的更新样式后,不立即生效,更新样式的合并,在下一次事件循环周期的渲染前将样式绑定到数据上,渲染结束后清空更新样式。

216078277264e2eddf45a4fcf004eb00.png

样式更新逻辑

在实现和实践图层的时候遇到了很多坑点,每个图层都可以长篇大论一番。下面就挑一些常见的,有通识性问题的图层做一些实践上的分享。

点图层

绘制地理的点数据,是在做地理信息可视化中的基本诉求,可以用图标、图形做标注,用文字做信息描述。

尺寸单位

在二维的地图中,点数据通常都是矢量呈现方式,即点的大小为像素数,随着地图缩放,点的大小和尺寸保持不变。在三维的地图中的绘制点的诉求就会产生分歧,常见的三维场景,通常都是近大选小的,即随着地图缩放(相机远近),点会和周围物体一样有视觉上的尺寸的变化,因此在做三维场景中地图的点,需要考虑点的尺寸单位是物理单位(米),还是像素单位(px)。

渲染朝向

除了尺寸单位,还需要考虑点的朝向,二维场景中,所有的特征都是朝向屏幕的,在三维场景中,随着相机的倾斜,会有场景需要场景中的数据点随之一起倾斜,这就需要在生成点的Mesh的时候,也将朝向考虑进去。

综合尺寸单位和渲染朝向,现在就有了4种排列组合方式:

  • 朝向屏幕,以像素为单位:常规需求,常见图标打点

       0ea69af7082b88eeebc2b553d2573263.gif

  • 朝向天空,以像素为单位:使用场景例如路的名称

       b3020da1bd2829d5140ef5fd51edab54.gif        

  • 朝向天空,以米为单位:应用场景例如扫描动画,表现物理影响范围

       5d457624665b74078ac44a6851d5360c.gif  

  • 朝向屏幕,以米为单位:适合固定规模的场景,缩小时不重叠

       9c49076215b1cb29656a1a193ce9d76c.gif

实现方案

对于每一个点,都用4个顶点+2个三角形面来表示,其中四个顶点的坐标在CPU中计算为同一个位置,增加anchor信息辅助描述顶点的拉伸方向,增加offset信息描述点的偏移量,这样就可以在顶点着色器中,动态根据朝向和尺寸计算三角形面的具体顶点。

   82a4ab760616a27a11191b966c618029.png

点面计算逻辑

朝向屏幕,以像素为单位的实现方式,与常规顶点计算类似,需要在计算完矩阵投影后,做顶点拉伸和加减偏移量。

// 朝向屏幕 + 像素单位vec2 r_anchor = m_ratation * (a_anchor * a_size);vec4 projected_position = projectionMatrix * modelViewMatrix * vec4(position.xyz, 1.);gl_Position = vec4(projected_position.xy / projected_position.w + (r_anchor * a_scale + offset ) / u_resolution * 2.0, 0.0,1.0);

同理的还有朝向屏幕,以米为单位的场景,需要在最后的拉伸再乘一个当前视窗像素与物理单位的比例。

// 朝向屏幕 + 物理单位float mileScale = dist * PXSCALE; vec3 newPosition = vec3(position.xy + a_offset * u_cameraScale, position.z);vec4 projected_position = projectionMatrix * modelViewMatrix * vec4(newPosition.xyz, 1.);gl_Position = vec4(projected_position.xy / projected_position.w + (r_anchor * a_scale) / mileScale / u_resolution, 0.0,1.0);

朝向天空,以米为单位,需要先算出点的拉伸和偏移,再做投影矩阵运算。

// 朝向天空 + 物理单位vec3 offsetPosition = vec3(position.xy + (r_anchor * vec2(1., -1.) * a_scale + a_offset) * u_cameraScale, position.z);gl_Position = projectionMatrix * modelViewMatrix * vec4(offsetPosition, 1.);

朝向天空,以像素为单位的类似,也需要先计算拉伸和偏移,此外需要多计算一个视窗像素与物理单位的比例。

// 朝向天空 + 像素单位float pxScale = dist * PXSCALE * u_cameraScale;vec3 offsetPosition = vec3(position.xy + (r_anchor * vec2(1., -1.) * a_scale + a_offset) * pxScale * 2.0, position.z);gl_Position = projectionMatrix * modelViewMatrix * vec4(offsetPosition, 1.);

OD弧线图层

在地图场景中,OD线通常用来绘制起点和终点之间的某种关系,例如表示人口迁徙、航班、DDOS攻击等。绘制OD线的难点,在于如何生成一根“面线”。

等宽线计算

在WebGL中,绘制一根有粗度的线,是需要计算线的面网格的。可以通过相邻的两个点,计算法线方向,沿着法线方向,做宽度的拉伸,从而实现一根“面线”。

d60767de402aafc7d48e7ea90d878df4.png

"面线"计算原理

如果需要朝向屏幕,按照像素单位宽,需要在着色器中动态计算线粗的拉伸值,具体的可以参考之前的文章《如何在WebGL中画一根2px的线》,此外,现在Three.js的官网扩展示例中,也提供了LineGeometry,也可以满足粗线的需求。

弧线计算

目前地理场景中弧线的计算,通常以大圆弧线居多。什么是大圆弧线呐?就是球面上两点最短的路径。在墨卡托投影完就是弧线的形状。大圆弧线路径的计算方式通常以二分法插值为主,即根据两点计算出中心点坐标,依次类推,计算出其他插值点。

4a635ad16e42b931bb324a13a9150969.png

二分法插值

大圆弧线也有其对应的弊端,就是在穿过北极或南极附近的线路,会表现异常。

708f7b4c8aaaffa16946d3d10c3c6ace.png

异常的大圆弧线

除此以外,平面的OD弧线,我们还使用了二次贝塞尔曲线计算弧线的方法。两点的OD线,将第三个控制点选择在两点中心的垂线方向上,从而得到贝塞尔曲线的三个控制点。

       a33b3e9bf9867da39806da752c24d401.png

贝塞尔控制点计算

三维场景的中的立体OD弧线也很常见,就是将弧度映射到海拔上(z轴),其插值点的计算方式也比较简单,将OD线的弧度均分N份,每份弧度的sin值可以计算出当前插值点的高度,根据弧度cos值可以推算当前插值点在OD线上的比例t,那么线段上任意一点,都可以通过(1-t) * a + t * b得到。

37e0367615905b003f090b03f18cc716.png

3D圆弧插值计算示意

应用场景

最终实现效果如下图(中央委员教育迁徙),用双色表示OD方向,黄色表示起点,白色表示终点。       

5e98b772eecbd655c8881dd057fcadf6.gif

中央委员教育迁徙OD图

热力图层

热力图是以颜色来表现数据强弱大小及分布趋势,在三维地图场景中,还可以借助高度来提升立体感。

实现热力图,需要做两个阶段的渲染,第一个阶段是密度的渲染,通常会用颜色的某一个通道,颜色叠加得到密度。我们使用到了点云颜色叠加高亮,利用r通道做密度的判断。需要注意的是,如果在地图缩放的过程中,想得到动态连续的热度变化,需要在视窗变化的时候对密度图持续更新。

 fcaae1606ec5502a30f41627ede73a32.png

密度图

第二阶段的渲染,其实体是一个网格平面。

7bfddfdfea7efdcb7ebe3694b713628e.png

热力网格wireframe

根据密度图的r通道,对应计算热力的颜色和高度值。在计算密度时,可以利用贝塞尔曲线对r做一些处理,使得在高度和颜色的变化上没有那么陡峭。

vec2 toBezier2(float t, vec2 P0, vec2 P1, vec2 P2, vec2 P3){  float t2 = t * t;  float one_minus_t = 1.0 - t;  float one_minus_t2 = one_minus_t * one_minus_t;  return (P0 * one_minus_t2 * one_minus_t + P1 * 3.0 * t * one_minus_t2 + P2 * 3.0 * t2 * one_minus_t + P3 * t2 * t);}vec2 toBezier(float t, vec4 p){  return toBezier2(t, vec2(0.0, 0.0), vec2(p.x, p.y), vec2(p.z, p.w), vec2(1.0, 1.0));}

在热力着色时,需要两张纹理,第一张是密度图,第二章是热力渐变纹理,在片元着色器中,将密度值用做uv的s向量取色就OK啦。

       1b41af92095406ebf9327ca661540fc2.png

热度渐变纹理

       5cc063adccfaaff9ebeced16cfa6649a.gif

热力图

其它可视化图层的残坑点和代码细节未来有机会再逐一展开分享。

往期回顾

打造服务B端客户的酷炫3D地图可视化产品

数据源与存储计算

地图交互与姿态控制

地图文字渲染

地图建筑渲染

地图建筑建模制作与输出

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我理解了你的问题。以下是我给出的答案: 首先,KMeans是一种常用的聚类算法,它可以将一组数据分成K个不同的簇,使得同一簇内的数据点彼此相似度较高,不同簇之间的数据点差异性较大。在这里,我将使用Python编程语言中的scikit-learn库来实现KMeans聚类,并使用matplotlib库进行可视化。 其次,为了演示KMeans聚类和可视化的效果,我将选择一个名为Wine的数据集。这个数据集包含了178个葡萄酒的样本,每个样本包含了13个特征:酒精、苹果酸、灰、灰的碱性、镁、总酚、黄烷醇、类黄酮、非黄烷类酚、原花青素、颜色强度、色调以及稀释葡萄酒的OD280 / OD315。这个数据集可以在scikit-learn库中直接获取。 接下来,我将给出Python代码实现KMeans聚类和可视化的过程。你可以按照以下步骤进行操作: 1. 导入所需的库: ```python from sklearn.datasets import load_wine from sklearn.cluster import KMeans import matplotlib.pyplot as plt ``` 2. 加载数据集: ```python data = load_wine() X = data.data ``` 3. 对数据进行KMeans聚类: ```python kmeans = KMeans(n_clusters=3, random_state=0).fit(X) ``` 4. 可视化聚类结果: ```python plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels_) plt.xlabel('Alcohol') plt.ylabel('Malic acid') plt.show() ``` 运行以上代码,就可以得到一个可视化的KMeans聚类结果。这个结果将葡萄酒样本分为了3个簇,并用不同颜色的点表示不同的簇。你可以根据需要对代码进行修改,以适应你自己的数据集。 希望这个回答能够帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值