本文适合对vue,arcgis4.x,threejs,ES6较熟悉的人群食用。

效果图:

素材:

主要思路:

先用arcgis externalRenderers封装了一个ExternalRendererLayer,在里面把arcgis和threejs的context关联,然后再写个子类继承它,这部分类容在上一个帖子里面有讲过。

子类AreaLayer继承它,并在里面实现绘制流光边界线的方法,我这里其实就是绘制城市区域的边界线。尝试过直线LineCurve3,三维二次贝塞尔曲线QuadraticBezierCurve3,三维三次贝塞尔曲线CubicBezierCurve3,结果感觉差不多=_=,所以最后还是用CatmullRomCurve3这个来构建管道,这个类使用也比其他的方便。

1:创建一个基于图片的材质

 1 const lineImg = require('../../../../public/static/img/line.png')
2 let lineTexture = new THREE.TextureLoader().load(lineImg)
3 lineTexture.wrapS = lineTexture.wrapT = THREE.RepeatWrapping; //每个都重复
4 lineTexture.repeat.set(1, 1)
5 lineTexture.needsUpdate = true
6
7 let lineMaterial = new THREE.MeshBasicMaterial({
8 map: lineTexture,
9 side: THREE.DoubleSide,
10 transparent: true
11 })

2:处理坐标转换数据

1 let linePoints = []
2 for(let i = 0; i < pointList.length; i++) {
3 var item = pointList[i];
4 var renderLinePoints = this.lngLatToXY(this.view, [item[0], item[1], 10]);
5 var vector3List = renderLinePoints.vector3List;
6
7 linePoints.push(new THREE.Vector3(vector3List.x, vector3List.y, vector3List.z));
8 }

3:构建TubeGeometry,创建Mesh

1 const curvePath = new THREE.CatmullRomCurve3(linePoints) // 曲线路径
2
3 let geometry = new THREE.TubeGeometry(curvePath, 64, 30, 8, true )
4 let lineMesh = new THREE.Mesh(geometry, lineMaterial);

4:最后再updateModels里面更新贴图的位置(其实就是render事件)。

1 updateModels(context) {
2 super.updateModels(context);
3
4 if (this.textures.length > 0) {
5 this.textures.forEach(texture => {
6 if (texture) texture.offset.x -= 0.01;
7 })
8 }
9 }

ExternalRendererLayer:

  1 import * as THREE from 'three'
2 import Stats from 'three/examples/jsm/libs/stats.module.js'
3 import * as webMercatorUtils from "@arcgis/core/geometry/support/webMercatorUtils"
4 import * as externalRenderers from "@arcgis/core/views/3d/externalRenderers"
5
6 export default class ExternalRendererLayer {
7 constructor({
8 view,
9 options
10 }) {
11 this.view = view
12 this.options = options
13
14 this.objects = []
15 this.scene = null
16 this.camera = null
17 this.renderer = null
18
19 this.setup();
20 }
21
22 setup() {
23 if (process.env.NODE_ENV !== "production") {
24 const sid = setTimeout(() => {
25 clearTimeout(sid)
26 //构建帧率查看器
27 let stats = new Stats()
28 stats.setMode(0)
29 stats.domElement.style.position = 'absolute'
30 stats.domElement.style.left = '0px'
31 stats.domElement.style.top = '0px'
32 document.body.appendChild(stats.domElement)
33 function render() {
34 stats.update()
35 requestAnimationFrame(render)
36 }
37 render()
38 }, 5000)
39 }
40 }
41
42 apply() {
43 let myExternalRenderer = {
44 setup: context => {
45 this.createSetup(context)
46 },
47 render: context => {
48 this.createRender(context)
49 }
50 }
51
52 externalRenderers.add(this.view, myExternalRenderer);
53 }
54
55 createSetup(context) {
56 this.scene = new THREE.Scene(); // 场景
57 this.camera = new THREE.PerspectiveCamera(); // 相机
58
59 this.setLight();
60
61 // 添加坐标轴辅助工具
62 const axesHelper = new THREE.AxesHelper(10000000);
63 this.scene.Helpers = axesHelper;
64 this.scene.add(axesHelper);
65
66 this.renderer = new THREE.WebGLRenderer({
67 context: context.gl, // 可用于将渲染器附加到已有的渲染环境(RenderingContext)中
68 premultipliedAlpha: false, // renderer是否假设颜色有 premultiplied alpha. 默认为true
69 // antialias: true
70 // logarithmicDepthBuffer: false
71 // logarithmicDepthBuffer: true
72 });
73 this.renderer.setPixelRatio(window.devicePixelRatio); // 设置设备像素比。通常用于避免HiDPI设备上绘图模糊
74 this.renderer.setViewport(0, 0, this.view.width, this.view.height); // 视口大小设置
75
76 // 防止Three.js清除ArcGIS JS API提供的缓冲区。
77 this.renderer.autoClearDepth = false; // 定义renderer是否清除深度缓存
78 this.renderer.autoClearStencil = false; // 定义renderer是否清除模板缓存
79 this.renderer.autoClearColor = false; // 定义renderer是否清除颜色缓存
80 // this.renderer.autoClear = false;
81
82 // ArcGIS JS API渲染自定义离屏缓冲区,而不是默认的帧缓冲区。
83 // 我们必须将这段代码注入到three.js运行时中,以便绑定这些缓冲区而不是默认的缓冲区。
84 const originalSetRenderTarget = this.renderer.setRenderTarget.bind(
85 this.renderer
86 );
87 this.renderer.setRenderTarget = target => {
88 originalSetRenderTarget(target);
89 if (target == null) {
90 // 绑定外部渲染器应该渲染到的颜色和深度缓冲区
91 context.bindRenderTarget();
92 }
93 };
94
95 this.addModels(context);
96
97 context.resetWebGLState();
98 }
99
100 createRender(context) {
101 const cam = context.camera;
102 this.camera.position.set(cam.eye[0], cam.eye[1], cam.eye[2]);
103 this.camera.up.set(cam.up[0], cam.up[1], cam.up[2]);
104 this.camera.lookAt(
105 new THREE.Vector3(cam.center[0], cam.center[1], cam.center[2])
106 );
107 // this.camera.near = 1;
108 // this.camera.far = 100;
109
110 // 投影矩阵可以直接复制
111 this.camera.projectionMatrix.fromArray(cam.projectionMatrix);
112
113 this.updateModels(context);
114
115 this.renderer.state.reset();
116
117 context.bindRenderTarget();
118
119 this.renderer.render(this.scene, this.camera);
120
121 // 请求重绘视图。
122 externalRenderers.requestRender(this.view);
123
124 // cleanup
125 context.resetWebGLState();
126 }
127
128 //经纬度坐标转成三维空间坐标
129 lngLatToXY(view, points) {
130
131 let vector3List; // 顶点数组
132
133 let pointXYs;
134
135
136 // 计算顶点
137 let transform = new THREE.Matrix4(); // 变换矩阵
138 let transformation = new Array(16);
139
140 // 将经纬度坐标转换为xy值\
141 let pointXY = webMercatorUtils.lngLatToXY(points[0], points[1]);
142
143 // 先转换高度为0的点
144 transform.fromArray(
145 externalRenderers.renderCoordinateTransformAt(
146 view,
147 [pointXY[0], pointXY[1], points[
148 2]], // 坐标在地面上的点[x值, y值, 高度值]
149 view.spatialReference,
150 transformation
151 )
152 );
153
154 pointXYs = pointXY;
155
156 vector3List =
157 new THREE.Vector3(
158 transform.elements[12],
159 transform.elements[13],
160 transform.elements[14]
161 )
162
163 return {
164 vector3List: vector3List,
165 pointXYs: pointXYs
166 };
167 }
168
169 setLight() {
170 console.log('setLight')
171 let ambient = new THREE.AmbientLight(0xffffff, 0.7);
172 this.scene.add(ambient);
173 let directionalLight = new THREE.DirectionalLight(0xffffff, 0.7);
174 directionalLight.position.set(100, 300, 200);
175 this.scene.add(directionalLight);
176 }
177
178 addModels(context) {
179 console.log('addModels')
180 }
181
182 updateModels(context) {
183 // console.log('updateModels')
184 }
185
186 }

AreaLayer:源码中mapx.queryTask是封装了arcgis的query查询,这个可以替换掉,我只是要接收返回的rings数组,自行构建静态数据也行

  1 import * as THREE from 'three'
2 import ExternalRendererLayer from './ExternalRendererLayer.js'
3 import Graphic from "@arcgis/core/Graphic";
4 import SpatialReference from '@arcgis/core/geometry/SpatialReference'
5 import * as externalRenderers from "@arcgis/core/views/3d/externalRenderers"
6
7 import mapx from '@/utils/mapUtils.js';
8
9 export default class AreaLayer extends ExternalRendererLayer {
10 constructor({
11 view,
12 options
13 }) {
14 super({
15 view,
16 options
17 })
18 }
19
20 setup() {
21 super.setup()
22
23 this.textures = []
24 }
25
26 addModels(context) {
27 // super.addModels(context)
28 // =====================mesh加载=================================//
29 const url = config.mapservice[1].base_url + config.mapservice[1].jd_url;
30 // const url = 'http://10.100.0.132:6080/arcgis/rest/services/wuchang_gim/gim_region/MapServer/2';
31 mapx.queryTask(url, {
32 where: '1=1',
33 returnGeometry: true
34 }).then(featureSet => {
35 if (featureSet.length > 0) {
36 featureSet.forEach(feature => {
37 const polygon = feature.geometry;
38 const rings = polygon.rings;
39 rings.forEach(ring => {
40 this._addModel(ring);
41 })
42 })
43 }
44 }).catch(error => {
45 console.log(error)
46 })
47 }
48
49 _addModel(pointList) {
50 const lineImg = require('../../../../public/static/img/line.png')
51 let lineTexture = new THREE.TextureLoader().load(lineImg)
52 lineTexture.wrapS = lineTexture.wrapT = THREE.RepeatWrapping; //每个都重复
53 lineTexture.repeat.set(1, 1)
54 lineTexture.needsUpdate = true
55
56 let lineMaterial = new THREE.MeshBasicMaterial({
57 map: lineTexture,
58 side: THREE.DoubleSide,
59 transparent: true
60 })
61
62 //确定几何体位置
63 let linePoints = []
64 // let curvePath = new THREE.CurvePath();
65 for(let i = 0; i < pointList.length; i++) {
66 var item = pointList[i];
67 var renderLinePoints = this.lngLatToXY(this.view, [item[0], item[1], 10]);
68 var vector3List = renderLinePoints.vector3List;
69
70 linePoints.push(new THREE.Vector3(vector3List.x, vector3List.y, vector3List.z));
71
72 // if(i < pointList.length - 1) {
73 // var item1 = pointList[i + 1];
74 // var renderLinePoints1 = this.lngLatToXY(this.view, [item1[0], item1[1], 10]);
75 // var vector3List1 = renderLinePoints1.vector3List;
76
77 // // var item2 = pointList[i + 1];
78 // // var renderLinePoints2 = this.lngLatToXY(this.view, [item2[0], item2[1], 10]);
79 // // var vector3List2 = renderLinePoints2.vector3List;
80
81 // const line = new THREE.LineCurve3(vector3List, vector3List1);
82 // // const line = new THREE.QuadraticBezierCurve3(vector3List, vector3List1, vector3List2);
83 // curvePath.curves.push(line)
84 // }
85 }
86
87 // console.log(curvePath)
88
89 // CatmullRomCurve3创建一条平滑的三维样条曲线
90 const curvePath = new THREE.CatmullRomCurve3(linePoints) // 曲线路径
91
92 let geometry = new THREE.TubeGeometry(curvePath, 64, 30, 8, true )
93 let lineMesh = new THREE.Mesh(geometry, lineMaterial);
94
95 this.scene.add(lineMesh);
96
97 this.textures.push(lineTexture);
98 this.objects.push(lineMesh);
99 }
100
101
102 updateModels(context) {
103 super.updateModels(context);
104
105 if (this.textures.length > 0) {
106 this.textures.forEach(texture => {
107 if (texture) texture.offset.x -= 0.01;
108 })
109 }
110 }
111
112 }

数据可视化【原创】vue+arcgis+threejs 实现流光边界线效果的更多相关文章

  1. PoPo数据可视化周刊第3期 - 台风可视化

    9月台风席卷全球,本刊特别选取台风最佳可视化案例,数据可视化应用功力最深厚者,当属纽约时报,而传播效果最佳的是The Weather Channel关于Florence的视频预报,运用了数据可视化.可 ...

  2. python数据可视化:pyecharts

    发现了一个做数据可视化非常好的库:pyecharts.非常便捷好用,大力推荐!! 官方介绍:pyecharts 是一个用于生成 Echarts 图表的类库.Echarts 是百度开源的一个数据可视化 ...

  3. 基于vue和echarts的数据可视化实现

    基于vue和echarts的数据可视化: https://github.com/MengFangui/awesome-vue.git

  4. [原创.数据可视化系列之五]韩国"萨德"系统防御图

    自从7月8日美国和韩国共同宣布将在韩国部署萨德反导系统后,韩国国内对此事的巨大争议以及本地区一些国家的强烈不满情绪在持续发酵.“萨德”(THAAD)全称“末段高空区域防御系统”,是美国导弹防御局和美国 ...

  5. [原创.数据可视化系列之八]使用等d3进行灰度图转伪彩色

    对灰度图进行彩色化是数据可视化中常见的需求,使用d3在客户端比较容易实现,本文使用d3生成图片,并显示: 代码如下: 代码中首先下载数据文件,然后设定d3的色带信息,生成一个空白的canvas元素,并 ...

  6. 地理数据可视化:Simple,Not Easy

    如果要给2015年的地理信息行业打一个标签,地理大数据一定是其中之一.在信息技术飞速发展的今天,“大数据”作为一种潮流铺天盖地的席卷了各行各业,从央视的春运迁徙图到旅游热点预测,从大数据工程师奇货可居 ...

  7. D3js初探及数据可视化案例设计实战

    摘要:本文以本人目前所做项目为基础,从设计的角度探讨数据可视化的设计的方法.过程和结果,起抛砖引玉之效.在技术方案上,我们采用通用web架构和d3js作为主要技术手段:考虑到项目需求,这里所做的可视化 ...

  8. 【Data Visual】一文搞懂matplotlib数据可视化

    一文搞懂matplotlib数据可视化 作者:白宁超 2017年7月19日09:09:07 摘要:数据可视化主要旨在借助于图形化手段,清晰有效地传达与沟通信息.但是,这并不就意味着数据可视化就一定因为 ...

  9. Python数据可视化的四种简易方法

    摘要: 本文讲述了热图.二维密度图.蜘蛛图.树形图这四种Python数据可视化方法. 数据可视化是任何数据科学或机器学习项目的一个重要组成部分.人们常常会从探索数据分析(EDA)开始,来深入了解数据, ...

  10. 倾斜摄影数据OSGB进入到ArcGIS平台相关问题小结

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zglybl/article/details/75252288      随着倾斜摄影技术的发展,大家 ...

随机推荐

  1. Java 世界的法外狂徒:反射

    概述 反射(Reflection)机制是指在运行时动态地获取类的信息以及操作类的成员(字段.方法.构造函数等)的能力.通过反射,我们可以在编译时期未知具体类型的情况下,通过运行时的动态查找和调用. 虽 ...

  2. LeetCode 周赛 346(2023/05/21)仅 68 人 AK 的最短路问题

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. LeetCode 单周赛第 345 场 · 体验一题多解的算法之美 单周赛 345 概览 T1. 删除子串后 ...

  3. Python基础 - 第一个python程序

    Python程序是什么? Python源程序就是一个特殊格式的文本文件,可以使用任意文本编辑器软件做python的开发,python的文件扩展名为 .py 执行python程序的三种方式 直接调用解释 ...

  4. kotlin 函数格式大赏

    fun main() { // 一个有引用的lambda表达式 val f11: (Int, Int) -> Unit = {n1, n2 -> println("f11 is ...

  5. jvm中类和对象定义存储基础知识

    1 类文件数据结构类型 Class文件结构主要有两种数据结构:无符号数和表 •无符号数:用来表述数字,索引引用.数量值以及字符串等,比如 图1中类型为u1,u2,u4,u8分别代表1个字节,2个字节, ...

  6. 【Leetcode】 two sum #1 for rust solution

    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标.你可以假设每种输入只会对应一个答案.但是,数组中同一个元素不能使用两遍. 示 ...

  7. 【技术积累】Vue.js中的基础概念与语法【一】

    写在前面 学习Vue之前最好有前端三驾马车的基础[HTML+CSS+JavaScript] 笔者接了一个从头开发的Vue项目,由于公司急着要,没有时间慢慢像在学校里学了,只能边学边做,现在项目雏形已经 ...

  8. 7. 特殊SQL的执行

    1. 模糊查询 ‍ 演示代码: /** * 测试模糊查询 * @param mohu * @return */ List<User> testMohu(@Param("mohu& ...

  9. 【linux命令】最强大的编辑器vim用法简介(基础篇)

    vim编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器.它主要分为命令令行模式.插入模式和底行模式这三种,下面主要介绍一下这三种模式最简单常用的用法. 一.命令 ...

  10. 在命令行按下tab键之后, 发生了生么?

    1. 引言 2. complete命令 3. 自定义补全列表 4. 动态补全列表 5. compgen命令 6. 别名的自动补全 7. 补全规则永久生效 8. 自动加载 9. 参考 1. 引言 当我们 ...