问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

北京到上海,Three.js旅行轨迹的可视化

发布网友 发布时间:2024-09-17 00:44

我来回答

1个回答

热心网友 时间:2024-11-11 18:28

最近从北京搬到了上海,开始了一段新的生活,算是人生中一个比较大的事件,于是特地用Three.js做了下可视化。

在这个地理信息相关的可视化的案例中,我们能学到地图怎么画、经纬度如何转成坐标值,这些是地理可视化的通用技术。

那我们就开始吧。

思路分析

Three.js画立方体、画圆柱、画不规则图形我们都画过,但是如何画一个地图呢?

其实地图也是由线、由多边形构成的,有了数据我们就能画出来,缺少的只是数据。

地图信息的描述是一个通用需求,所以有相应的国际标准,就是GeoJson,它是通过点、线、多边形来描述地理信息的。

通过指定点、线、多边形的类型、然后指定几个坐标位置,就可以描述出相应的形状。

geojson的数据可以通过geojson.io这个网站做下预览。

比如中国地图的geojson:

有了这个json,只要用Three.js画出来就行,通过线和多边形两种方式。

但是还有一个问题,geojson中记录的是经纬度信息,应该如何转成二维坐标来画呢?

这就涉及到了墨卡托转换,它就是做经纬度转二维坐标的事情。

这个转换也不用我们自己实现,可以用d3内置的墨卡托坐标转换函数来做。

这样,我们就用Three.js根据geojson来画出地图。

我们还要画一条北京到上海的曲线,这个用贝塞尔曲线画就行,知道两个端点的坐标,控制点放在中间的位置。

那怎么知道两个端点,也就是上海和北京的坐标呢?

这个可以用“百度坐标拾取系统”这个工具,点击地图的某个位置,就可以直接拿到那个位置的经纬度。然后我们做一次墨卡托转换,就拿到坐标了。

地图画出来了,旅行的曲线也画出来了,接下来调整下相机位置,从北京慢慢移动到上海就可以了。

思路理清了,我们来写下代码。

代码实现

我们要引入d3,然后使用d3的墨卡托转换功能,

constprojection=d3.geoMercator().center([116.412318,39.909843]).translate([0,0]);

中间点的坐标就是北京的经纬度,就是我们通过“百度坐标拾取工具”那里拿到的。

北京和上海的坐标位置也可以把经纬度做墨卡托转换得到:

letbeijingPosition=projection([116.412318,39.909843]);letshanghaiPosition=projection([121.495721,31.236797]);

先不着急画旅行的曲线,先来画地图吧。

先加载geojson:

constloader=newTHREE.FileLoader();loader.load('./data/china.json',(data)=>{constjsondata=JSON.parse(data);generateGeometry(jsondata);})

然后根据json的信息画地图。

遍历geojson的数据,把每个经纬度通过墨卡托转换变成坐标,然后分别用线和多边形画出来。

画多边形的时候遇到北京和上海用黄色,其他城市用蓝色。

functiongenerateGeometry(jsondata){constmap=newTHREE.Group();jsondata.features.forEach((elem)=>{constprovince=newTHREE.Group();//经纬度信息constcoordinates=elem.geometry.coordinates;coordinates.forEach((multiPolygon)=>{multiPolygon.forEach((polygon)=>{//画轮廓线constline=drawBoundary(polygon);//画多边形constprovinceColor=['北京市','上海市'].includes(elem.properties.name)?'yellow':'blue';constmesh=drawExtrudeMesh(polygon,provinceColor);province.add(line);province.add(mesh);});});map.add(province);})scene.add(map);}

然后分别实现画轮廓线和画多边形:

轮廓线(Line)就是指定一系列顶点来构成几何体(Geometry),然后指定材质(Material)颜色为黄色:

functiondrawBoundary(polygon){constlineGeometry=newTHREE.Geometry();for(leti=0;i<polygon.length;i++){const[x,y]=projection(polygon[i]);lineGeometry.vertices.push(newTHREE.Vector3(x,-y,0));}constlineMaterial=newTHREE.LineBasicMaterial({color:'yellow'});returnnewTHREE.Line(lineGeometry,lineMaterial);}

现在的效果是这样的:

多边形是ExtrudeGeometry,也就是可以先画出形状(shape),然后通过拉伸变成三维的。

functiondrawExtrudeMesh(polygon,color){constshape=newTHREE.Shape();for(leti=0;i<polygon.length;i++){const[x,y]=projection(polygon[i]);if(i===0){shape.moveTo(x,-y);}shape.lineTo(x,-y);}constgeometry=newTHREE.ExtrudeGeometry(shape,{depth:0,bevelEnabled:false});constmaterial=newTHREE.MeshBasicMaterial({color,transparent:true,opacity:0.2,})returnnewTHREE.Mesh(geometry,material);}

第一个点用moveTo,后面的点用lineTo,这样连成一个多边形,然后指定厚度为0,指定侧面不需要多出一块斜面(bevel)。

这样,我们就给每个省都填充上了颜色,北京和上海是黄色,其余省是蓝色。

接下来,在北京和上海之间画一条贝塞尔曲线:

constline=drawLine(beijingPosition,shanghaiPosition);scene.add(line);

贝塞尔曲线用QuadraticBezierCurve3来画,控制点指定中间位置的点。

functiondrawLine(pos1,pos2){const[x0,y0,z0]=[...pos1,0];const[x1,y1,z1]=[...pos2,0];constgeomentry=newTHREE.Geometry();geomentry.vertices=newTHREE.QuadraticBezierCurve3(newTHREE.Vector3(-x0,-y0,z0),newTHREE.Vector3(-(x0+x1)/2,-(y0+y1)/2,-10),newTHREE.Vector3(-x1,-y1,z1),).getPoints();constmaterial=newTHREE.LineBasicMaterial({color:'white'});constline=newTHREE.Line(geomentry,material);line.rotation.y=Math.PI;returnline;}

这样,地图和旅行轨迹就都画完了:

当然,还有渲染器、相机、灯光的初始化代码:

渲染器:

constrenderer=newTHREE.WebGLRenderer();renderer.setClearColor(0x000000);renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(renderer.domElement);

渲染器设置背景颜色为黑色,画布大小为窗口大小。

灯光:

letambientLight=newTHREE.AmbientLight(0xffffff);scene.add(ambientLight);

灯光用环境光,也就是每个方向的明暗都一样。

相机:

letbeijingPosition=projection([116.412318,39.909843]);letshanghaiPosition=projection([121.495721,31.236797]);0

相机用透视相机,特点是近大远小,需要指定看的角度,宽高比,和远近的范围这样四个参数。

位置设置在0010的位置,在这个位置去观察000,就是北京上方的俯视图(我们做墨卡托转换的时候指定了北京为中心)。

修改了相机位置之后,看到的地图大了许多:

接下来就是一帧帧的渲染,在每帧渲染的时候移动下相机位置,这样就是从北京到上海的一个移动的效果:

letbeijingPosition=projection([116.412318,39.909843]);letshanghaiPosition=projection([121.495721,31.236797]);1

大功告成!我们来看下最终的效果吧:

代码上传到了github:https://github.com/QuarkGluonPlasma/threejs-exercize

也在这里贴一份:

letbeijingPosition=projection([116.412318,39.909843]);letshanghaiPosition=projection([121.495721,31.236797]);2总结

地图形状的表示是基于geojson的规范,它是由点、线、多边形等信息构成的。

用Three.js或者其他绘制方式来画地图只需要加载geojson的数据,然后通过线和多边型把每一部分画出来。

画之前还要把经纬度转成坐标,这需要用到墨卡托转换。

我们用Three.js画线是通过指定一系列顶点构成Geometry,而画多边形是通过绘制一个形状,然后用ExtrudeGeometry(挤压几何体)拉伸成三维。墨卡托转换直接使用了d3的内置函数。旅行的效果是通过一帧帧的移动相机位置来实现的。

熟悉了geojson和墨卡托转换,就算是入门地理相关的可视化了。

你是否也想做一些和地理相关的可视化或者交互呢?不妨来尝试下吧。

来源:公众号「神光的编程秘籍」

热心网友 时间:2024-11-11 18:29

最近从北京搬到了上海,开始了一段新的生活,算是人生中一个比较大的事件,于是特地用Three.js做了下可视化。

在这个地理信息相关的可视化的案例中,我们能学到地图怎么画、经纬度如何转成坐标值,这些是地理可视化的通用技术。

那我们就开始吧。

思路分析

Three.js画立方体、画圆柱、画不规则图形我们都画过,但是如何画一个地图呢?

其实地图也是由线、由多边形构成的,有了数据我们就能画出来,缺少的只是数据。

地图信息的描述是一个通用需求,所以有相应的国际标准,就是GeoJson,它是通过点、线、多边形来描述地理信息的。

通过指定点、线、多边形的类型、然后指定几个坐标位置,就可以描述出相应的形状。

geojson的数据可以通过geojson.io这个网站做下预览。

比如中国地图的geojson:

有了这个json,只要用Three.js画出来就行,通过线和多边形两种方式。

但是还有一个问题,geojson中记录的是经纬度信息,应该如何转成二维坐标来画呢?

这就涉及到了墨卡托转换,它就是做经纬度转二维坐标的事情。

这个转换也不用我们自己实现,可以用d3内置的墨卡托坐标转换函数来做。

这样,我们就用Three.js根据geojson来画出地图。

我们还要画一条北京到上海的曲线,这个用贝塞尔曲线画就行,知道两个端点的坐标,控制点放在中间的位置。

那怎么知道两个端点,也就是上海和北京的坐标呢?

这个可以用“百度坐标拾取系统”这个工具,点击地图的某个位置,就可以直接拿到那个位置的经纬度。然后我们做一次墨卡托转换,就拿到坐标了。

地图画出来了,旅行的曲线也画出来了,接下来调整下相机位置,从北京慢慢移动到上海就可以了。

思路理清了,我们来写下代码。

代码实现

我们要引入d3,然后使用d3的墨卡托转换功能,

constprojection=d3.geoMercator().center([116.412318,39.909843]).translate([0,0]);

中间点的坐标就是北京的经纬度,就是我们通过“百度坐标拾取工具”那里拿到的。

北京和上海的坐标位置也可以把经纬度做墨卡托转换得到:

letbeijingPosition=projection([116.412318,39.909843]);letshanghaiPosition=projection([121.495721,31.236797]);

先不着急画旅行的曲线,先来画地图吧。

先加载geojson:

constloader=newTHREE.FileLoader();loader.load('./data/china.json',(data)=>{constjsondata=JSON.parse(data);generateGeometry(jsondata);})

然后根据json的信息画地图。

遍历geojson的数据,把每个经纬度通过墨卡托转换变成坐标,然后分别用线和多边形画出来。

画多边形的时候遇到北京和上海用黄色,其他城市用蓝色。

functiongenerateGeometry(jsondata){constmap=newTHREE.Group();jsondata.features.forEach((elem)=>{constprovince=newTHREE.Group();//经纬度信息constcoordinates=elem.geometry.coordinates;coordinates.forEach((multiPolygon)=>{multiPolygon.forEach((polygon)=>{//画轮廓线constline=drawBoundary(polygon);//画多边形constprovinceColor=['北京市','上海市'].includes(elem.properties.name)?'yellow':'blue';constmesh=drawExtrudeMesh(polygon,provinceColor);province.add(line);province.add(mesh);});});map.add(province);})scene.add(map);}

然后分别实现画轮廓线和画多边形:

轮廓线(Line)就是指定一系列顶点来构成几何体(Geometry),然后指定材质(Material)颜色为黄色:

functiondrawBoundary(polygon){constlineGeometry=newTHREE.Geometry();for(leti=0;i<polygon.length;i++){const[x,y]=projection(polygon[i]);lineGeometry.vertices.push(newTHREE.Vector3(x,-y,0));}constlineMaterial=newTHREE.LineBasicMaterial({color:'yellow'});returnnewTHREE.Line(lineGeometry,lineMaterial);}

现在的效果是这样的:

多边形是ExtrudeGeometry,也就是可以先画出形状(shape),然后通过拉伸变成三维的。

functiondrawExtrudeMesh(polygon,color){constshape=newTHREE.Shape();for(leti=0;i<polygon.length;i++){const[x,y]=projection(polygon[i]);if(i===0){shape.moveTo(x,-y);}shape.lineTo(x,-y);}constgeometry=newTHREE.ExtrudeGeometry(shape,{depth:0,bevelEnabled:false});constmaterial=newTHREE.MeshBasicMaterial({color,transparent:true,opacity:0.2,})returnnewTHREE.Mesh(geometry,material);}

第一个点用moveTo,后面的点用lineTo,这样连成一个多边形,然后指定厚度为0,指定侧面不需要多出一块斜面(bevel)。

这样,我们就给每个省都填充上了颜色,北京和上海是黄色,其余省是蓝色。

接下来,在北京和上海之间画一条贝塞尔曲线:

constline=drawLine(beijingPosition,shanghaiPosition);scene.add(line);

贝塞尔曲线用QuadraticBezierCurve3来画,控制点指定中间位置的点。

functiondrawLine(pos1,pos2){const[x0,y0,z0]=[...pos1,0];const[x1,y1,z1]=[...pos2,0];constgeomentry=newTHREE.Geometry();geomentry.vertices=newTHREE.QuadraticBezierCurve3(newTHREE.Vector3(-x0,-y0,z0),newTHREE.Vector3(-(x0+x1)/2,-(y0+y1)/2,-10),newTHREE.Vector3(-x1,-y1,z1),).getPoints();constmaterial=newTHREE.LineBasicMaterial({color:'white'});constline=newTHREE.Line(geomentry,material);line.rotation.y=Math.PI;returnline;}

这样,地图和旅行轨迹就都画完了:

当然,还有渲染器、相机、灯光的初始化代码:

渲染器:

constrenderer=newTHREE.WebGLRenderer();renderer.setClearColor(0x000000);renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(renderer.domElement);

渲染器设置背景颜色为黑色,画布大小为窗口大小。

灯光:

letambientLight=newTHREE.AmbientLight(0xffffff);scene.add(ambientLight);

灯光用环境光,也就是每个方向的明暗都一样。

相机:

letbeijingPosition=projection([116.412318,39.909843]);letshanghaiPosition=projection([121.495721,31.236797]);0

相机用透视相机,特点是近大远小,需要指定看的角度,宽高比,和远近的范围这样四个参数。

位置设置在0010的位置,在这个位置去观察000,就是北京上方的俯视图(我们做墨卡托转换的时候指定了北京为中心)。

修改了相机位置之后,看到的地图大了许多:

接下来就是一帧帧的渲染,在每帧渲染的时候移动下相机位置,这样就是从北京到上海的一个移动的效果:

letbeijingPosition=projection([116.412318,39.909843]);letshanghaiPosition=projection([121.495721,31.236797]);1

大功告成!我们来看下最终的效果吧:

代码上传到了github:https://github.com/QuarkGluonPlasma/threejs-exercize

也在这里贴一份:

letbeijingPosition=projection([116.412318,39.909843]);letshanghaiPosition=projection([121.495721,31.236797]);2总结

地图形状的表示是基于geojson的规范,它是由点、线、多边形等信息构成的。

用Three.js或者其他绘制方式来画地图只需要加载geojson的数据,然后通过线和多边型把每一部分画出来。

画之前还要把经纬度转成坐标,这需要用到墨卡托转换。

我们用Three.js画线是通过指定一系列顶点构成Geometry,而画多边形是通过绘制一个形状,然后用ExtrudeGeometry(挤压几何体)拉伸成三维。墨卡托转换直接使用了d3的内置函数。旅行的效果是通过一帧帧的移动相机位置来实现的。

熟悉了geojson和墨卡托转换,就算是入门地理相关的可视化了。

你是否也想做一些和地理相关的可视化或者交互呢?不妨来尝试下吧。

来源:公众号「神光的编程秘籍」
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
王卡专属免流包是什么意思 腾讯王卡专属流量是什么意思 人防临空墙排烟机房可以留洞吗? 瑞士转机的问题? 雅阁、第九代凯美瑞、迈腾,大空间的中级车推荐 云顶之弈双人排位介绍 金铲铲之战 金铲铲段位相差多少可以一起打? 现在上海办理居住证需要本人去吗 34周加5天是几个月 孕34周是几个月了 孕34周如何预防早产 threejs3d环形? 第167期:threejs最简单的例子 threejs介绍(threejs介绍) 十一想去黄骅海钓,不乘船出海了!在哪调合适? 今年想去黄骅去海钓,本人没在海边钓过,想先在边上试一下,明白人,给几... 大家给个建议。学校校门口用检测器查手机。有什么方法过 改名字在哪改?花钱吗?要家长陪同或签字吗? 急需《鲁宾逊漂流记》的故事梗概(200~300)字,谢谢了!! gpon和10gpon有什么区别 ...业务板上的模块最多支持多少个ONU,最大分光比是多少 我儿子七岁左下颚长了个囊肿该怎么办 七岁儿童右贤囊肿38x3.4可以治疗吗 我儿子前额七岁时检查出有蛛网膜囊肿。但是复查到现在都没有长大的... 七岁的孩子得了腘窝囊肿,不想做手术、请问有没有好的办法? 左侧额顶部交界处蛛网膜囊肿怎么办 新时代大学生如何弘扬中国优秀传统文化? 如何弘扬和传承中国传统优秀文化 诸葛亮有复原图吗?有的话求一张 我的手机之前更新系统卡住,我强制关机,白苹果了,后来我用itunes恢复_百... 路面h是什么意思? WebGL衍生的第三方库:ThingJS和threejs两个3D框架比较 血氧饱和度85的危害 红米怎么卡刷降级 红米note11pro怎么降级系统版本 飞行器设计与工程就业情况怎么样,难度大吗,一般上什么单位,工资怎么... 飞行器设计与工程专业有什么条件限制 哪些交友聊天软件可以免费使用? 吊兰怎么养才能更旺盛呢?学会这些方法,让你的吊兰长到停不下来哦!_百... 吊兰应该怎么养才能更旺盛?教你养出美丽优雅又健康的吊兰! 如何让吊兰长得快 吊兰怎么养殖才能更旺盛 不露脸唱歌可以赚钱吗? 山西临汾古县农村社保卡交的250元钱能累计吗? 农村社保卡一年280刚刚交能使用吗? 杜高能猎狍子吗,狍子比野猪多吧。 成都周边适合旅游的城市,资阳到广汉途中的旅游景点 荷兰股市叫什么 招聘顾问招聘顾问的主要职责 研究院院长英语怎么说 ...最恰当的一组是 [ ] 1982年,他做了核武器研究院院长以