Vue(3.3.4)+three.js(0.161.0)实现3D可视化地图

一.前言


        由于最近在学习three.js,所以观摩了一下掘金,csdn等网站上的有关这部分的内容,刚好看到一个带你入门three.js——从0到1实现一个3d可视化地图 - 掘金 (juejin.cn),再加上我的专业属性是地理相关,可以说是专业对口,但文章已经是三年以前写的,而且没有在框架底下完成,有关three的很多API也发生了更改,所以我的思路是来自该篇文章,我进行了模仿和相应的修改,但是大致没有发生改变,可以说是站在前人的肩膀上。

二.预览


 

三.实现


         首先就是开启一个vue项目,再npm install --save three,再引入一下d3就可以了,配置方面没有什么好配置的,这方面大家应该是没问题的。将代码写在子组件里,再引入到App.vue中展示就可以了。需要注意用到的全国的json数据来自DataV.GeoAtlas地理小工具系列 (aliyun.com)

子组件xx.vue对应代码

<template><div id="container" ref="canvasContainer"></div><div id="tooltip" ref="tooltip"></div>
</template><script setup>import * as THREE from 'three';//OrbitControls 是一个附加组件,必须显式导入import { OrbitControls } from 'three/addons/controls/OrbitControls.js';//墨卡托投影转换可以把我们经纬度坐标转换成我们对应平面的2d坐标,d3里面自带墨卡托投影转换//该引入方式是查阅官网得到的import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";import { onMounted, onUnmounted,ref } from 'vue';let canvasContainer = ref(null);let tooltip = ref(null)let scene,camera,renderer,ambientLight,raycaster,mouse;let lastPick = null;//初始化摄像机function initCamera(){camera = new THREE.PerspectiveCamera(75,canvasContainer.value.offsetWidth / canvasContainer.value.offsetHeight, 0.1, 1000);camera.position.set(0,0,120);camera.lookAt(scene.position);}//初始化rendererfunction initRenderer(){renderer = new THREE.WebGLRenderer();renderer.setSize(canvasContainer.value.offsetWidth,canvasContainer.value.offsetHeight)}//初始化灯光function initLight(){ambientLight = new THREE.AmbientLight(0xffffff,20);}//加载json数据function loadJson(){const loader = new THREE.FileLoader();loader.load('src/assets/中华人民共和国.json',(data)=>{const jsondata = JSON.parse(data);generateGeometry(jsondata)console.log(jsondata);})}// 根据JSON数据生成地图几何体function generateGeometry(jsondata){let map = new THREE.Object3D();// 使用d3的地图投影const projection = d3.geoMercator().center([104.0,37.5]).translate([0,0]);// 遍历每个省份,创建几何体jsondata.features.forEach((element)=>{let province = new THREE.Object3D();const coordinates = element.geometry.coordinates;if(Array.isArray(coordinates[0][0][0])){coordinates.forEach((multiPolygon)=>{multiPolygon.forEach((polygon)=>{const shape = new THREE.Shape();const points = [];polygon.forEach((coord,i)=>{const [x,y] = projection(coord);if(i===0) shape.moveTo(x,-y);else shape.lineTo(x,-y);points.push(new THREE.Vector3(x,-y,5));})const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);const lineMaterial = new THREE.LineBasicMaterial({ color: 'white' });const line = new THREE.Line(lineGeometry, lineMaterial);const extrudeSettings = { depth: 10, bevelEnabled: false };const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);const material = new THREE.MeshBasicMaterial({ color: '#2defff', transparent: true, opacity: 0.6 });const material1 = new THREE.MeshBasicMaterial({color: '#3480C4',transparent: true,opacity: 0.5,})const mesh = new THREE.Mesh(geometry, [material,material1]);province.properties = element.properties;province.add(mesh);province.add(line);})})}else if(Array.isArray(coordinates[0][0])){coordinates.forEach((polygon)=>{const shape = new THREE.Shape();const points = [];polygon.forEach((coord,i)=>{const [x,y] = projection(coord);if(i===0) shape.moveTo(x,-y);else shape.lineTo(x,-y);points.push(new THREE.Vector3(x,-y,5));})const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);const lineMaterial = new THREE.LineBasicMaterial({ color: 'white' });const line = new THREE.Line(lineGeometry, lineMaterial);const extrudeSettings = { depth: 10, bevelEnabled: false };const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);const material = new THREE.MeshBasicMaterial({ color: '#2defff', transparent: true, opacity: 0.6 });const material1 = new THREE.MeshBasicMaterial({color: '#3480C4',transparent: true,opacity: 0.5,})const mesh = new THREE.Mesh(geometry, [material,material1]);province.properties = element.properties;province.add(mesh);province.add(line);})}map.add(province);})scene.add(map);}// 设置光线投射器和鼠标位置,用于检测鼠标悬停对象function setRaycaster(){raycaster = new THREE.Raycaster();mouse = new THREE.Vector2();const onMouseMove = (event) => {mouse.x = (event.clientX / canvasContainer.value.offsetWidth) * 2 - 1mouse.y = -(event.clientY / canvasContainer.value.offsetHeight) * 2 + 1tooltip.value.style.left = event.clientX + 2 + 'px'tooltip.value.style.top = event.clientY + 2 + 'px'}window.addEventListener('mousemove', onMouseMove, false)}// 显示或隐藏工具提示function showTip(){if(lastPick){const properties = lastPick.object.parent.properties;tooltip.value.textContent = properties.name;tooltip.value.style.visibility = 'visible';console.log(tooltip.value.textContent);}else{tooltip.value.style.visibility = 'hidden';}}// 动画循环,用于渲染场景和更新状态function animate() {requestAnimationFrame(animate);raycaster.setFromCamera(mouse,camera);const intersects = raycaster.intersectObjects(scene.children,true);if (lastPick) {lastPick.object.material[0].color.set('#2defff')lastPick.object.material[1].color.set('#3480C4')}lastPick = nulllastPick = intersects.find((item) => item.object.material && item.object.material.length === 2)if (lastPick) {lastPick.object.material[0].color.set(0xff0000)lastPick.object.material[1].color.set(0xff0000)}showTip();renderer.render(scene, camera);}//窗口大小改变时,更新摄像机的宽高比和渲染器的大小function handleResize(){if(camera && renderer && canvasContainer.value){camera.aspect = canvasContainer.value.offsetWidth / canvasContainer.value.offsetHeight;camera.updateProjectionMatrix();renderer.setSize(canvasContainer.value.offsetWidth, canvasContainer.value.offsetHeight);}}// 组件挂载时的初始化逻辑onMounted(()=>{scene = new THREE.Scene();setRaycaster();initLight();scene.add(ambientLight);initCamera();loadJson();initRenderer();canvasContainer.value.appendChild(renderer.domElement);new OrbitControls(camera,canvasContainer.value)animate();window.addEventListener('resize',handleResize)})onUnmounted(()=>{window.removeEventListener('resize',handleResize)})
</script><style>body{margin: 0;padding: 0;overflow: hidden;}#container{/* border: 1px solid black; */width: 100vw;height: 100vh;}#tooltip {position: absolute;z-index: 2;background: white;padding: 10px;border-radius: 5px;visibility: hidden;}
</style>

注意在用JSON数据生成地图集合体时分两种情况是因为:

不同省份数据数组嵌套的层数不一样,类似于下面这两地

 

四.总结

        共勉,如果对于实现的步骤还有疑惑,可以转至我在前言分享的那篇文章 ,它对于实现步骤更详细,可以结合着看。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/713053.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

存储xss实现获取cookie(本地实战)

实战更能体验收获&#xff01;&#xff01;&#xff01; 环境准备&#xff1a; 1.phpstudy 2.dvwa靶场 实战 首先我们在phpstudy指定的localhost网站目录下编写一个xss.php文件&#xff0c;内容如下&#xff1a; <?php $cookie $_GET[cookie]; $ip getenv (REMOTE_…

electron+vue3全家桶+vite项目搭建【28】封装窗口工具类【2】窗口组,维护窗口关系

文章目录 引入实现效果思路主进程模块渲染进程模块测试效果 引入 demo项目地址 窗口工具类系列文章&#xff1a; 封装窗口工具类【1】雏形 我们思考一下窗口间的关系&#xff0c;窗口创建和销毁的一些动作&#xff0c;例如父子窗口&#xff0c;窗口组合等等&#xff0c;还有…

【前端素材】推荐优质在线高端家具电商网页Classi平台模板(附源码)

一、需求分析 1、系统定义 在线高端家具商城是一个专门销售高端家具产品的电子商务平台&#xff0c;旨在为消费者提供购买高品质家具的便捷渠道。 2、功能需求 在线高端家具商城是一个专门销售高端家具产品的电子商务平台&#xff0c;旨在为消费者提供购买高品质家具的便捷…

Maven高级(黑马学习笔记)

Maven 是一款构建和管理 Java 项目的工具。 分模块设计与开发 介绍 所谓分模块设计&#xff0c;顾名思义指的就是我们在设计一个 Java 项目的时候&#xff0c;将一个 Java 项目拆分成多个模块进行开发。 1). 未分模块设计的问题 如果项目不分模块&#xff0c;也就意味着所有…

node.js和electron安装

文章目录 一、node.js安装1.node.js下载安装2.设置镜像 二、其它问题1.文件夹创建错误2.electron安装错误 一、node.js安装 1.node.js下载安装 参考B站视频node.js安装&#xff0c;没有按视频中设置镜像 2.设置镜像 参考&#xff1a;https://npmmirror.com/ npm config se…

十八:Java8新特性

文章目录 01、Java8概述02、Java8新特性的好处03、并行流与串行流04、Lambda表达式4.1、Lambda表达式使用举例4.2、Lambda表达式语法的使用14.3、Lambda表达式语法的使用2 05、函数式(Functional)接口5.1、函数式接口的介绍5.2、Java内置的函数式接口介绍及使用举例 06、方法引…

Hgame题解(第二星期)

Hgame题解&#xff08;第二星期&#xff09; Web Select More Courses 打开靶机发现是一个登陆页面&#xff0c;根据题目提示下载弱密码字典&#xff0c;通过BP爆破获得用户密码为qwert123 登陆后进入下一个页面&#xff0c;由于学分已满无法选课&#xff0c;所以需要先进行…

回归预测 | Matlab实现BiTCN基于双向时间卷积网络的数据回归预测

回归预测 | Matlab实现BiTCN基于双向时间卷积网络的数据回归预测 目录 回归预测 | Matlab实现BiTCN基于双向时间卷积网络的数据回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现BiTCN基于双向时间卷积网络的数据回归预测&#xff08;完整源码和数据&a…

推荐莹莹API管理系统PHP源码

莹莹API管理系统PHP源码附带两套模板,PHP版本要求为5.6至8.0之间&#xff0c;已测试通过的版本为7.4。 需要安装PHPSG11加密扩展。 已测试&#xff1a;宝塔/主机亲测成功搭建&#xff01; 演示地 址 &#xff1a; runruncode.com/php/19698.html 安装说明&#xff08;适用于宝…

深入理解c指针(六)

目录 九、函数指针数组 1、字符指针变量 2、数组指针变量 3、二维数组传参的本质 4、函数指针变量 4.1 分析《C陷阱和缺陷》中的两端代码 4.2 typedef关键字 5、函数指针数组 6、函数指针数组的用途---转移表 九、函数指针数组 1、字符指针变量 在指针的类型中我们知道…

【C++精简版回顾】15.继承派生

1.继承派生的区别 继承&#xff1a;子继父业&#xff0c;就是子类完全继承父类的全部内容 派生&#xff1a;子类在父类的基础上发展 2.继承方式 1.public继承为原样继承 2.protected继承会把public继承改为protect继承 3.private继承会把public&#xff0c;protected继承改为pr…

怎么抠图把把人物扣下来?简单快捷的抠图方法

相信很多新手小白在初入设计行业时&#xff0c;对于抠图怎么把人物扣下来都是一头雾水。抠图作为设计中常用的一种技术&#xff0c;能够帮助我们快速提取图片中的某个部分&#xff0c;进行合成或者修改。对于老手来说&#xff0c;抠图或许是再熟悉不过的操作&#xff0c;但对于…

c语言---数组(超级详细)

数组 一.数组的概念二. 一维数组的创建和初始化2.1数组的创建2.2数组的初始化错误的初始化 2.3 数组的类型 三. 一维数组的使用3.1数组的下标3.2数组元素的打印3.2数组元素的输入 四. 一维数组在内存中的存储五. 二维数组的创建5.1二维数组的概念5.2如何创建二维数组 六.二维数…

【嵌入式学习】QT-Day4-Qt基础

简单实现闹钟播报&#xff0c;设置时间&#xff0c;当系统时间与设置时间相同时播报语音5次&#xff0c;然后停止。如果设置时间小于当前系统时间&#xff0c;则弹出消息提示框&#xff0c;并清空输入框。 #include "my_clock.h" #include "ui_my_clock.h&quo…

批量处理图片,像素随心所欲,创意无限释放!

在数字化时代&#xff0c;图片批量处理已成为设计、摄影、电商等多个领域不可或缺的一部分。然而&#xff0c;传统的图片批量处理方式往往效率低下&#xff0c;难以满足现代人对高效和精准的需求。现在&#xff0c;我们为您带来了一款一键图片批量处理工具&#xff0c;让您自由…

【Vue】更换浏览器默认 logo

更换浏览器默认logo为自定义图片 一. 浏览器默认 logo二. 替换为自定义logo三. 步骤3.1 转换大小3.1.1 查看图片尺寸3.1.2 修改尺寸&#xff08;为32px 32px&#xff09; 3.2 替换成功 一. 浏览器默认 logo 二. 替换为自定义logo 三. 步骤 3.1 转换大小 将自定义 logo 转为323…

docker搭建zookeeper集群

文章目录 1. 集群搭建2. Leader选举3. Zookeeper集群角色 1. 集群搭建 这里我们使用docker-compose 搭建伪集群 version: 3.1 services:zoo1:image: zookeeperrestart: alwayscontainer_name: zoo1ports:- 2181:2181volumes:- /home/zk/zoo1/data:/data- /home/zk/zoo1/datal…

React富文本编辑器开发(二)

我们接着上一节的示例内容&#xff0c;现在有如下需求&#xff0c;我们希望当我们按下某个按键时编辑器有所反应。这就需要我们对编辑器添加事件功能onKeyDown, 我们给 Editor添加事件&#xff1a; SDocor.jsx import { useState } from react; import { createEditor } from…

协方差矩阵计算

文章目录 协方差矩阵计算原理python实现 协方差矩阵 协方差矩阵反映了两个随机变量变化时是同向还是反向的&#xff08;相关性&#xff09;。 如果协方差>0&#xff0c;则说明这两个随机变量同向变化。 协方差矩阵<0&#xff0c;则说明是反向变化。 协方差矩阵0&#xf…

【LeetCode】347.前 K 个高频元素

今日学习的文章链接和视频链接 leetcode题目地址&#xff1a;347.前 K 个高频元素 代码随想录题解地址&#xff1a;代码随想录 题目简介 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 看到题目的第一…