threejs教程:绘制3D地图(广东省区划图)

news2024/11/24 5:32:27

一、效果展示:

二、开发准备

Three.js中文文档:Three.js中文网

Three.js文本渲染插件:Troika 3D Text - Troika JS

行政区划边界数据查询(阿里云数据可视化平台):DataV.GeoAtlas地理小工具系列

1. 在项目中添加 `threejs` 依赖

npm install three

2. 添加相关声明依赖,为Typescript项目提供类型提示和类型检查的支持

npm install @types/three -D

3. 添加 `troika-three-text` 依赖,本案例中用于绘制所有市名文字,如无需绘制文字可不用

npm install troika-three-text

4.  使用阿里云数据可视化平台的范围选择器工具查询你所需要的区划边界数据,点击右下角其他类型的蓝色下载图标下载JSON文件,并导入项目中。本案例以广东省为例, `@/src/assets/广东省.json`。

三、代码实现

1.  添加canvas组件和基础样式


<template>
  <div>
    <canvas id="three" />
  </div>
</template>

<style scoped>
#three {
  width: 100vw;
  height: 100vh;
  position: absolute;
  top: 0;
  left: 0;
}
</style>

2. 导入three,创建三维场景并设置背景颜色,案例中的蓝底即为此处设置的颜色。

import * as THREE from 'three';
import { onMounted } from 'vue';

onMounted(() => {
  // 创建3D场景对象Scene
  const scene = new THREE.Scene();
  // 设置背景颜色
  scene.background = new THREE.Color("#a4cdff");
});

3. Threejs要把三维场景Scene渲染到web网页上需创建个虚拟相机

// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera(
  30,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
// 设置相机在Three.js三维坐标系中的位置,此处修改z坐标为调整正面远近,值越小越近
camera.position.z = 200;

THREE.PerspectiveCamera构造函数的参数说明

4.  有了场景和相机后,关键还需要一个渲染器去生成影像

// 创建渲染器对象
const threeDemo = document.getElementById("three");
const renderer = new THREE.WebGLRenderer({ canvas: threeDemo, antialias: true });

5. 处理每一帧动画和分辨率问题

function resizeDevicePixel(renderer: THREE.WebGLRenderer) {
  const canvas = renderer.domElement
  const width = window.innerWidth
  const height = window.innerHeight
  const devicePixelWidth = canvas.width / window.devicePixelRatio
  const devicePixelHeight = canvas.height / window.devicePixelRatio

  const needResize = devicePixelWidth !== width || devicePixelHeight !== height
  if (needResize) {
    renderer.setSize(width, height, false)
  }
  return needResize
}

// Three.js 需要一个动画循环函数,Three.js 的每一帧都会执行这个函数
function annimate() {
  renderer.render(scene, camera);
  requestAnimationFrame(annimate);
  // 矫正设备的物理像素分辨率与CSS像素分辨率的比值,解决模糊问题
  if(resizeDevicePixel(renderer)) {
    const canvas = renderer.domElement;
    camera.aspect = canvas.clientWidth / canvas.clientHeight;
    camera.updateProjectionMatrix();
  }
}
annimate();

6. 要让模型动起来还需添加轨道控制器

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
const controls = new OrbitControls(camera, renderer.domElement);
controls.update();

7.以上便完成了所有初始配置,开始进入正题,渲染3D广东省区划图

导入之前下载的广东省区划边界数据:

import GuangDong from './assets/广东省.json';

遍历所有城市边界数据生成对应板块模型 

let xy = [0, 0, 0, 0];
GuangDong.features.forEach((city, index) => {
  let boundingBox, x = 0, y = 0;
  // 遍历城市的所有块边界数据(城市可能包含陆和岛多个块组成)
  city.geometry.coordinates.forEach((coordinate, cindex) => {
    const data = coordinate[0];
    // 一组二维向量表示一个多边形轮廓坐标
    const pointsArr = data.map(e => new THREE.Vector2(e[0] * 10, e[1] * 10));
    // Shape表示一个平面多边形轮廓,参数是二维向量构成的数组pointsArr
    const shape = new THREE.Shape(pointsArr);
    // 多边形shape轮廓作为ShapeGeometry参数,生成一个多边形几何体
    const geometry = new THREE.ExtrudeGeometry(shape, {
      depth: 2, // 这里设置形状的厚度
      bevelEnabled: false, // 是否开启倒角,默认关闭
    });

    // 计算几何体的中心点并居中
    geometry.computeBoundingBox();
    // 计算平面几何体的中心点
    const { min, max } = geometry.boundingBox;
    xy[2] = (min.x + max.x) / 2;
    xy[3] = (min.y + max.y) / 2;
    if (index === 0) { // 记录第一个几何体的位置,作为其他几何体的位置参考点
      xy[0] = xy[2];
      xy[1] = xy[3];
    }
    // 移动几何体以便中心点位于原点
    const center = new THREE.Vector3();
    geometry.boundingBox!.getCenter(center);
    geometry.translate(-center.x + (xy[2] - xy[0]), -center.y + (xy[3] - xy[1]), -1);
    // 记录每个城市核心板块的位置信息
    if (cindex === 0) {
      boundingBox = geometry.boundingBox;
      x = xy[2];
      y = xy[3];
    }
    let material = new THREE.MeshBasicMaterial({ color: "#fcf9f2" });
    let mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);

    // 从原始几何体中获取边缘几何体
    const edgesGeometry = new THREE.EdgesGeometry(geometry);
    // 创建一个具有宽度的线条材质(这里我们使用LineBasicMaterial并设置linewidth)
    const edgeMaterial = new THREE.LineBasicMaterial({
        color: 0xcccccc, // 白色边缘
        linewidth: 1, // 边缘宽度
    });
    // 创建线段对象来渲染边缘线条
    const edgeLines = new THREE.LineSegments(edgesGeometry, edgeMaterial);
    scene.add(edgeLines);
  });

  // 渲染市名
  const text = new Text();
  text.text = city.properties.name;
  text.fontSize = 1;
  text.color = 0x333333;
  text.geometry.computeBoundingBox();
  const textCenter = new THREE.Vector3();
  text.geometry.boundingBox.getCenter(textCenter);
  // 计算市中心位置
  const point = city.properties.center
  const { min: { x: minX, y: minY }, max: { x: maxX, y: maxY } } = boundingBox;
  const c1 = [(minX + maxX) / 2, (minY + maxY) / 2];
  const c2 = [(point[0] * 10 - x) + c1[0], (point[1] * 10 - y) + c1[1]];
  // 把市名文字绘制到市中心点
  text.position.set(c2[0] + 0.3, c2[1] + 0.3, 1.2);
  scene.add(text);

  // 在场景中创建圆环
  const ringGeometry = new THREE.RingGeometry(0.4, 0.3, 32);
  // 创建圆环的材质
  const ringMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, side: THREE.DoubleSide});
  const ring = new THREE.Mesh(ringGeometry, ringMaterial);
  // 把圆环绘制到市中心点
  ring.position.set(c2[0]-0.2, c2[1]-0.2, 1.1);
  scene.add(ring);

  // 在场景中创建圆形
  const circleGeometry = new THREE.CircleGeometry(0.2, 32);
  // 创建圆形的材质
  const circleMaterial = new THREE.MeshBasicMaterial({color: 0xff0000});
  const circle = new THREE.Mesh(circleGeometry, circleMaterial);
  // 把圆形直接加到圆环的中心点
  ring.add(circle);
});

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

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

相关文章

JavaFX 分隔符

Separator类表示水平或垂直分隔线。它分割元素&#xff0c;不产生任何动作。 我们可以设计风格&#xff0c;应用视觉效果&#xff0c;并为分隔符设置动画。 默认情况下&#xff0c;分隔符是水平的。我们可以使用setOrientation方法改变它的方向。 Separator类扩展了Node类。…

Android sensor列表和访问记录

命令: dumpsys sensorservice 1.dumpsys sensorservice查看最近申请记录 dumpsys sensorservice命令输出Previous Registrations. Previous Registrations: 23:07:43 0x00000008 pid16587 uid10397 packagecom.start.testdemo.ui.udfp.fql.XsqFQLActivity samplingPeriod66…

如何避免WordPress更新导致的网站崩溃

我查阅过很多关于如何确保 WordPress 网站安全的建议&#xff0c;其中最常提到的就是及时更新 WordPress。 我按照建议更新了网站&#xff0c;结果却导致网站崩溃。 网站的瘫痪后&#xff0c;我甚至连WordPress仪表板都无法登陆&#xff0c;只留下了一条让我困惑的错误信息&a…

Git快速上手

初识Git 是一个免费开源, 分布式的代码版本控制系统, 帮助开发团队维护代码 作用: 记录代码内容,切换代码版本,多人开发时高效合并代码内容 Git和GitHub Git是一个软件, Github是一个网站,两者的功能都是提供版本控制服务. 官网: GitHub: Let’s build from here GitHub …

Linux系统下多网卡多网关设置

场景一&#xff1a; 主机AB得网卡1和网卡2都分别划分在VLAN1和VLAN2中&#xff0c;主机C在VLAN3中&#xff0c;VLAN1&#xff0c;2&#xff0c;3在三层交换设备上配置好网关192.168.1.1 192.168.2.1 192.168.3.1&#xff0c;并开启三层交换功能。 主机A的两块网卡分别IP为192…

JavaScript Window对象之(BOM、JS 执行机制、location对象、navigator对象、histroy对象、本地存储)

目录 1. BOM(浏览器对象模型)2. JS 执行机制3. location对象4. navigator对象5. histroy对象6. 本地存储6.1 localStorage6.2 sessionStorage 1. BOM(浏览器对象模型) BOM(Browser Object Model)是浏览器对象模型。其中window对象是JavaScript中一个全局的顶级对象基本的BOM属性…

26 种 prompt 套路,驯服大模型

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、算法项目落地经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 总结链接如…

微服务中的相关概念

Eureka Eureka 是由 Netflix 开发的一个服务发现和注册中心&#xff0c;广泛应用于微服务架构中。Eureka 主要用于管理和协调分布式服务的注册和发现&#xff0c;确保各个服务之间能够方便地找到并通信。它是 Netflix OSS&#xff08;Netflix Open Source Software&#xff09…

Java基础 - 练习(二)打印菱形

Java基础练习 打印菱形&#xff0c;先上代码&#xff1a; // 方法一&#xff1a;基础&#xff0c;好理解 public static void diamond() {//控制行数for (int i 1; i < 4; i) {//空格的个数for (int k 1; k < 4 - i; k) {System.out.print(" ");}//控制星星…

【科研必备神器】人口迁徙大数据科研辅助利器安装使用教程

今天&#xff0c;给大家介绍一款用于对人口迁徙大数据进行分析的神器--人口迁徙数据解析器&#xff0c;可以获取对目标城市迁入、迁出的迁徙数据详情&#xff0c;如&#xff0c;排名前100名的迁徙来源地的迁徙时间、城市名、省份名及迁徙比例。下面&#xff0c;给大家介绍下这款…

Google推出开源代码大模型CodeGemma:AI编程新纪元,代码自动完成和生成技术再升级

论文标题: CodeGemma: Open Code Models Based on Gemma机构: Google LLC论文链接: https://arxiv.org/pdf/2406.11409.pdf CodeGemma模型概述 CodeGemma是基于Google DeepMind的Gemma模型系列&#xff08;Gemma Team et al., 2024&#xff09;开发的一系列开放代码模型。这些…

RK3588/算能/Nvidia智能盒子:加速山西铝业智能化转型,保障矿业皮带传输安全稳定运行

近年来&#xff0c;各类矿山事故频发&#xff0c;暴露出传统矿业各环节的诸多问题。随着全国重点产煤省份相继出台相关政策文件&#xff0c;矿业智能化建设进程加快。皮带传输系统升级是矿业智能化的一个重要环节&#xff0c;同时也是降本增效的一个重点方向。 △各省份智能矿山…

Linux中DNS搭建

文章目录 一、DNS介绍1.1、DNS是什么1.2、DNS的工作原理1.3、DNS的域名结构 二、Bind介绍2.1、bind概述2.2、bind主要配置文件 三、DNS安装四、主要配置文件解析&#xff08;除/etc/named.conf&#xff09;4.1、/etc/named.rfc1912.zones4.2、/etc/rc.d/init.d/named4.3、/etc/…

git idea分支cherry-pick

git idea分支cherry-pick cherry-pick请注意操作前更新代码&#xff01;&#xff01;&#xff01;操作步骤 cherry-pick cherry-pick 挑拣樱桃&#xff0c;对应在分支开发中就是把提交记录从A分支挑拣到B分支 请注意操作前更新代码&#xff01;&#xff01;&#xff01; 操作…

kettle从入门到精通 第七十一课 ETL之kettle 再谈http post,轻松掌握body中传递json参数

场景&#xff1a; kettle中http post步骤如何发送http请求且传递body参数&#xff1f; 解决方案&#xff1a; http post步骤中直接设置Request entity field字段即可。 1、手边没有现成的post接口&#xff0c;索性用python搭建一个简单的接口&#xff0c;关键代码如下&#…

JavaFX按钮

当用户单击按钮时&#xff0c;JavaFX Button类可以触发事件。Button类扩展了Labeled类&#xff0c;可以显示文本&#xff0c;图像或两者都可以。 以下代码显示了如何向Button添加单击操作侦听器。 import javafx.application.Application; import javafx.event.ActionEvent; im…

[14] CUDA_使用Opencv处理图像

CUDA_使用Opencv处理图像 1. Opencv中的图像表示 Opencv 提供了Mat 类来存储图像&#xff0c;如下&#xff1a; cv::Mat img; imgcv::imread("cameraman.tif);定义图像的示例&#xff1a; //定义单通道图像 cv::Mat img(6,6,CV_8UC1); //32位浮点型 Mat img2(256,256,…

异步爬虫:aiohttp 异步请求库使用:

使用requests 请求库虽然可以完成爬虫业务&#xff0c;但是对于异步任务来说&#xff0c;它是做不到的&#xff0c; 这时候我们需要借助 aiohttp 异步请求库来完成异步爬虫的编写&#xff1a; 话不多说&#xff0c;直接看示例&#xff1a; 注意&#xff1a;楼主使用的python版…

MyBatis逆向工程和MyBatisX插件的使用

文章目录 1.ORM思维2.逆向工程3.MyBatisX插件的使用 1.ORM思维 ORM&#xff08;Object-Relational Mapping&#xff0c;对象-关系映射&#xff09;是一种将数据库和面向对象编程语言中的对象之间进行转换的技术。它将对象和关系数据库的概念进行映射&#xff0c;最后我们就可以…

同三维T80006EH单路高清HDMI编码器

同三维T80006EH单路高清HDMI编码器 1路HDMI输入&#xff0c;1路3.5音频输入和输出&#xff0c;支持高清1080P60&#xff0c;支持SD卡录制 支持可解1路网络音频流输出&#xff0c;双向互动 一、产品简介&#xff1a; T80006EH高清编码器&#xff08;采集盒&#xff09;是一款…