当前位置:   article > 正文

threejs+vue 省份3D可视化地图_threejs三维地图可视化

threejs三维地图可视化

先上效果图

在这里插入图片描述
在这里插入图片描述

需要查找地图的josn数据、需要引入的js、 和需要安装的依赖如下

1.先获取想要展示地图的区域json数据
阿里云获取地图区域的json
示例为陕西省
在这里插入图片描述

2.npm安装three.js和d3 npm i threenpm i d3
3.引入相关方法和json数据

import * as THREE from 'three';
import { OrbitControls } from "../../../node_modules/three/examples/jsm/controls/OrbitControls.js"
import { TextGeometry } from '../../../node_modules/three/examples/jsm/geometries/TextGeometry.js';
import { FontLoader } from '../../../node_modules/three/examples/jsm/loaders/FontLoader.js';
import * as d3 from 'd3';
import jsondata from './shanxi.json'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

具体代码

<template>
    <div class="center-map-box" id="contant">
    </div>
</template>
  • 1
  • 2
  • 3
  • 4
 mounted() {
		// 第一步新建一个场景
        this.scene = new THREE.Scene()
        this.contant = document.getElementById('contant')
        // 辅助线
        // const axesHelper = new THREE.AxesHelper(10);
        // this.scene.add(axesHelper);
        // 光源
        this.spotLight = new THREE.PointLight('#fff', 4, 100)
        this.spotLight.position.set(0.2, -0.4, 1)
        this.scene.add(this.spotLight)
        //环境光
        const ambient = new THREE.AmbientLight('#fff', 4)
        this.scene.add(ambient)
        // 可视化点光源
        // const pointLightHelper = new THREE.PointLightHelper(this.spotLight, 0.1)
        // this.scene.add(pointLightHelper)
        this.setCamera()
        this.setRenderer()
        this.generateGeometry()
        this.setClickFn()
        this.setController()
        this.animate()
        window.onresize = () => {
            this.renderer.setSize(this.contant.clientWidth, this.contant.clientHeight);
            this.camera.aspect = this.contant.clientWidth / this.contant.clientHeight;
            this.camera.updateProjectionMatrix();
        };
 }
  methods: {
  	 // 新建透视相机
        setCamera() {
            this.camera = new THREE.PerspectiveCamera(60, this.contant.clientWidth / this.contant.clientHeight, 0.1, 500);
            this.camera.position.z = 10
        },
        // 设置渲染器
        setRenderer() {
            this.renderer = new THREE.WebGLRenderer()
            // 设置画布的大小
            this.renderer.setSize(this.contant.clientWidth, this.contant.clientHeight)
            //这里 其实就是canvas 画布  renderer.domElement
            this.contant.appendChild(this.renderer.domElement)
            this.renderer.setClearColor(0x000000, 0)
        },
        generateGeometry() {
            // 初始化一个地图对象
            this.map = new THREE.Object3D()

            // 墨卡托投影转换
            const projection = d3
                .geoMercator()
                .center([104.0, 37.5])
                .scale(80)
                .translate([0, 0])

            jsondata.features.forEach((elem) => {
                this.renderer.render(this.scene, this.camera);
                const coordinates = elem.geometry.coordinates
                // 循环坐标数组
                coordinates.forEach((multiPolygon) => {
                    multiPolygon.forEach((polygon, index) => {
                        const province = new THREE.Object3D()
                        const shape = new THREE.Shape()
                        const lineMaterial = new THREE.LineBasicMaterial({
                            color: 'white',
                        })
                        const lineGeometry = new THREE.BufferGeometry()
                        const pointsArray = new Array()
                        for (let i = 0; i < polygon.length; i++) {
                            const [x, y] = projection(polygon[i])
                            if (i === 0) {
                                shape.moveTo(x, -y)
                            }
                            shape.lineTo(x, -y)
                            pointsArray.push(new THREE.Vector3(x, -y, 0))
                        }
                        lineGeometry.setFromPoints(pointsArray)


                        const extrudeSettings = {
                            depth: 0.07,
                            bevelEnabled: false,
                        }

                        const geometry = new THREE.ExtrudeGeometry(
                            shape,
                            extrudeSettings
                        )
                        const material = new THREE.MeshPhongMaterial({
                            color: '#43A7FF',
                            transparent: true,
                            opacity: 0.8,
                        })
                        const material1 = new THREE.MeshBasicMaterial({
                            color: '#3480C4',
                            transparent: true,
                            opacity: 0.4,
                        })
                        const loader = new FontLoader();
                        //字体需放到根目录public下
                        loader.load('./fonts/FZCuHeiSongS-B-GB_Regular.json', (font) => {
                            const fontOption = {
                                font: font,
                                size: 0.07,
                                height: 0.01,
                                curveSegments: 1,
                                bevelThickness: 1,
                                bevelSize: 0,
                                bevelEnabled: false,
                                bevelSegments: 0
                            };
                            const txtMater = new THREE.MeshBasicMaterial({ color: 0xffffff });
                            const txtGeometry = new TextGeometry(name, fontOption);
                            const txtMesh = new THREE.Mesh(txtGeometry, txtMater);
                            const [x, y] = projection(elem.properties.center)
                            txtMesh.position.set(x - 8.3, -y + 4.4, 0.08)
                            if (name == 'xx县') {
                            //这里位置不对可以做微调
                                txtMesh.position.set(x - 8.33, -y + 4.55, 0.08)
                            }
                            this.scene.add(txtMesh);
                        });
                        var name = elem.properties.name;//区县名
                        const mesh = new THREE.Mesh(geometry, [material, material1])
                        const line = new THREE.Line(lineGeometry, lineMaterial)
                        this.materialArr.push(material)
                        province.properties = elem.properties
                        province.add(mesh)
                        province.add(line)
                        this.map.add(province)
                        this.render()
                    })
                })
            })
            this.map.position.set(-8.2, 4.4, 0);
            this.scene.add(this.map);
            this.spotLight.target = this.map;
            this.camera.position.set(0, -0.7, 2.5);
            this.renderer.render(this.scene, this.camera);

        },
        //加事件
        setClickFn() {
            this.raycaster = new THREE.Raycaster();
            this.mouse = new THREE.Vector2();
            const onMouseMove = (event) => {
                var marginLeft = this.contant.offsetLeft
                var marginTop = this.contant.offsetTop + 92
                // 如果该地图不是占满全屏需要减去margintop和marginleft
                // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
                // this.mouse.x = (event.clientX / this.contant.clientWidth) * 2 - 1;
                // this.mouse.y = -(event.clientY / this.contant.clientHeight) * 2 + 1;
                this.mouse.x = ((event.clientX - marginLeft) / this.contant.clientWidth) * 2 - 1;
                this.mouse.y = -((event.clientY - marginTop) / this.contant.clientHeight) * 2 + 1;
            };

            let clickPosition;
            window.addEventListener("mousemove", onMouseMove, false);
            const onclick = (event) => {
                var marginLeft = this.contant.offsetLeft
                var marginTop = this.contant.offsetTop
                // let x = (event.clientX / this.contant.clientWidth) * 2 - 1;
                // let y = -(event.clientY / this.contant.clientHeight) * 2 + 1;
                // 如果该地图不是占满全屏需要减去margintop和marginleft
                let x = ((event.clientX - marginLeft) / this.contant.clientWidth) * 2 - 1;
                let y = -((event.clientY - marginTop) / this.contant.clientHeight) * 2 + 1;
                clickPosition = { x: x, y: y };
                this.raycaster.setFromCamera(clickPosition, this.camera);
                // 算出射线 与当场景相交的对象有那些
                const intersects = this.raycaster.intersectObjects(this.scene.children, true);
                let clickObj = intersects.find(
                    (item) => item.object.material && item.object.material.length === 2
                );
                // 点击区县
                if (clickObj && clickObj.object) {
                	console.log(clickObj)
                    // this.$emit('clickAreaCheck',clickObj)
                }
            };
            window.addEventListener("mousedown", onclick, false);
        },
        // 设置最大旋转的角度
        setController() {
            const controls = new OrbitControls(this.camera, this.renderer.domElement);
            controls.maxPolarAngle = 2.5
            controls.minPolarAngle = 1
            controls.maxAzimuthAngle = 1
            controls.minAzimuthAngle = -1
            controls.addEventListener("change", () => {
                this.renderer.render(this.scene, this.camera);
            });
        },
         animate() {
            window.requestAnimationFrame(this.animate);
            this.raycaster.setFromCamera(this.mouse, this.camera);
            this.renderer.render(this.scene, this.camera);
        },
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198

该文章只做记录,具体在场景中使用中自己调整。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
  

闽ICP备14008679号