天天看點

Threejs實作繪制地球,地理位置标注-經緯度轉換threejs坐标

1,介紹

該示例使用的是 r95版本Three.js庫。

主要實作功能:繪制地球和地理位置進行标注 

效果圖如下: 

Threejs實作繪制地球,地理位置标注-經緯度轉換threejs坐标

2,主要說明 

準備一張地圖,建立一個球體并進行貼圖,把地理位置經緯度轉換成threejs的世界坐标,并進行标注。

建立地球,部分代碼如下:

// 建立地球 半徑100
function createEarth() {
	var earthGeo = new THREE.SphereGeometry(radius, 50, 50);
	var earthMater = new THREE.MeshPhongMaterial({
		map: new THREE.TextureLoader().load('assets/earth/earth3.jpg'),
		transparent: true,
		depthWrite: false,
		side: THREE.DoubleSide,
		blending: THREE.AdditiveBlending,
		opacity: 0.8,
		color: 0x03d98e
	});
	var earthMesh = new THREE.Mesh(earthGeo, earthMater);
	scene.add(earthMesh)
}
           

經緯度轉換成threejs坐标(也叫右手坐标系) 

Threejs實作繪制地球,地理位置标注-經緯度轉換threejs坐标

 phi是方位面(水準面)内的角度,範圍0~360度,theta是俯仰面(豎直面)内的角度,範圍0~180度、就是空間極坐标系中的兩個參數,和類比直角坐标系裡的xyz

Threejs實作繪制地球,地理位置标注-經緯度轉換threejs坐标

threejs實作轉換,代碼如下:

// 坐标轉換,
function createPosition(lnglat) {
	let spherical = new THREE.Spherical
	spherical.radius = radius;
	const lng = lnglat[0]
	const lat = lnglat[1]
	const theta = (lng + 90) * (Math.PI / 180)
	const phi = (90 - lat) * (Math.PI / 180)
	spherical.phi = phi; // phi是方位面(水準面)内的角度,範圍0~360度
	spherical.theta = theta; // theta是俯仰面(豎直面)内的角度,範圍0~180度
	let position = new THREE.Vector3()
	position.setFromSpherical(spherical)
	return position
}
           

 3,源碼

<!DOCTYPE html>
<html>
	<head>
		<title>Threejs實作繪制地球,地理位置标注-經緯度轉換世界坐标</title>
		<script type="text/javascript" src="libs/three.js"></script>
		<script type="text/javascript" src="libs/OrbitControls.js"></script>
		<style>
			body {
				margin: 0;
				overflow: hidden;
			}
		</style>
	</head>
	<body>
		<div id="dom"></div>
		<script type="text/javascript">
			var camera;
			var renderer;
			var radius = 100; // 地球半徑
			var areas = [{
				name: "中國",
				position: [116.20, 39.55]
			}, {
				name: "中非共和國",
				position: [18.35, 4.23]
			}, {
				name: "智利",
				position: [-70.40, -33.24]
			}, {
				name: "查德",
				position: [14.59, 12.10]
			}, {
				name: "尚比亞",
				position: [28.16, -15.28]
			}, {
				name: "越南",
				position: [105.55, 21.05]
			}, {
				name: "約旦",
				position: [35.52, 31.57]
			}, {
				name: "英屬維爾京群島",
				position: [-64.37, 18.27]
			}, {
				name: "英國",
				position: [-0.05, 51.36]
			}];

			function init() {
				// 建立一個場景,它将包含我們所有的元素,如物體,相機和燈光。
				var scene = new THREE.Scene();

				var urls = [
					'assets/textures/cubemap/flowers/posx.jpg',
					'assets/textures/cubemap/flowers/negx.jpg',
					'assets/textures/cubemap/flowers/posy.jpg',
					'assets/textures/cubemap/flowers/negy.jpg',
					'assets/textures/cubemap/flowers/posz.jpg',
					'assets/textures/cubemap/flowers/negz.jpg'
				];

				var cubeLoader = new THREE.CubeTextureLoader();
				// scene.background = cubeLoader.load(urls);

				// 建立一個錄影機,它定義了我們正在看的地方
				camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
				// 将錄影機對準場景的中心
				camera.position.z = 500;
				camera.lookAt(scene.position);
				var orbit = new THREE.OrbitControls(camera);

				// 建立一個渲染器并設定大小,WebGLRenderer将會使用電腦顯示卡來渲染場景
				renderer = new THREE.WebGLRenderer({
					antialias: true,
					logarithmicDepthBuffer: true,
				});
				renderer.setSize(window.innerWidth, window.innerHeight);
				// scene.add(new THREE.AmbientLight(0x666666));

				var ambientLight = new THREE.AmbientLight("#ffffff", 1);
				scene.add(ambientLight);

				// 在螢幕上顯示坐标軸
				var axes = new THREE.AxisHelper(radius);
				scene.add(axes);

				// 将平面添加到場景中
				var plane = createPlaneGeometryBasicMaterial();
				// scene.add(plane);

				// initSphere(10, 0, 10);
				createEarth();
				createAreaPoint();

				var rs = coordinateWorldTurnScreen(10, 0, 10);
				var div = document.createElement('div');
				div.innerHTML = '立';
				div.style.padding = '5px';
				div.style.position = 'absolute';
				div.style.backgroundColor = 'rgba(155,0,155,0.8)';
				document.body.appendChild(div);
				div.style.left = rs.x + "px";
				div.style.top = rs.y + "px";
				console.log(rs)

				// 将呈現器的輸出添加到HTML元素
				document.getElementById("dom").appendChild(renderer.domElement);

				// 啟動動畫
				renderScene();

				/**
				 * 建立地面并添加材質
				 * wrapS屬性定義的是紋理沿x軸方向的行為,而warpT屬性定義的是紋理沿y軸方向的行為。
				 * Three.js為這些屬性提供了如下兩個選項:
				 * ·THREE.RepeatWrapping允許紋理重複自己。
				 * ·THREE.ClampToEdgeWrapping是屬性的預設值。
				 * 屬性值為THREE.ClampToEdgeWrapping時,那麼紋理的整體不會重複,隻會重複紋理邊緣的像素來填滿剩下的空間。
				 */
				function createPlaneGeometryBasicMaterial() {
					var textureLoader = new THREE.TextureLoader();
					var cubeMaterial = new THREE.MeshStandardMaterial({
						map: textureLoader.load("assets/textures/stone/cd.jpg"),
					});
					cubeMaterial.map.wrapS = THREE.RepeatWrapping;
					cubeMaterial.map.wrapT = THREE.RepeatWrapping;
					cubeMaterial.map.repeat.set(8, 8)
					// 建立地平面并設定大小
					var planeGeometry = new THREE.PlaneGeometry(100, 100);
					var plane = new THREE.Mesh(planeGeometry, cubeMaterial);

					// 設定平面位置并旋轉
					plane.rotation.x = -0.5 * Math.PI;
					plane.position.x = 0;
					plane.position.z = 0;
					return plane;
				}

				// 世界坐标轉螢幕坐标
				function coordinateWorldTurnScreen(x, y, z) {
					let world_vector = new THREE.Vector3(x, y, z);
					let vector = world_vector.project(camera);
					let halfWidth = window.innerWidth / 2,
						halfHeight = window.innerHeight / 2;
					return {
						x: Math.round(vector.x * halfWidth + halfWidth),
						y: Math.round(-vector.y * halfHeight + halfHeight)
					}
				}

				// 建立地球 半徑100
				function createEarth() {
					var earthGeo = new THREE.SphereGeometry(radius, 50, 50);
					var earthMater = new THREE.MeshPhongMaterial({
						map: new THREE.TextureLoader().load('assets/earth/earth3.jpg'),
						transparent: true,
						depthWrite: false,
						side: THREE.DoubleSide,
						blending: THREE.AdditiveBlending,
						opacity: 0.8,
						color: 0x03d98e
					});
					var earthMesh = new THREE.Mesh(earthGeo, earthMater);
					scene.add(earthMesh)
				}

				function createAreaPoint() {
					// 球面
					let sphereGeom = new THREE.SphereGeometry(1, 20, 20),
						sphereMat = new THREE.MeshBasicMaterial({
							color: 0x03d98e,
							wireframe: true
						})
					let sphere = new THREE.Mesh(sphereGeom, sphereMat)
					scene.add(sphere)
					// 地标及光錐
					for (let i = 0, length = areas.length; i < length; i++) {
						const position = createPosition(this.areas[i].position)
						createHexagon(position); // 地标
					}
				}

				// 坐标轉換,
				function createPosition(lnglat) {
					let spherical = new THREE.Spherical
					spherical.radius = radius;
					const lng = lnglat[0]
					const lat = lnglat[1]
					const theta = (lng + 90) * (Math.PI / 180)
					const phi = (90 - lat) * (Math.PI / 180)
					spherical.phi = phi; // phi是方位面(水準面)内的角度,範圍0~360度
					spherical.theta = theta; // theta是俯仰面(豎直面)内的角度,範圍0~180度
					let position = new THREE.Vector3()
					position.setFromSpherical(spherical)
					return position
				}

				// 建立地标标記
				function createHexagon(position) {
					var hexagon = new THREE.Object3D()
					let hexagonLine = new THREE.CircleGeometry(4, 6)
					let hexagonPlane = new THREE.CircleGeometry(3, 6)
					let vertices = hexagonLine.vertices
					vertices.shift() // 第一個節點是中心點
					let material = new THREE.MeshBasicMaterial({
						color: 0xffff00,
						side: THREE.DoubleSide,
						opacity: 0.5
					})
					let circleLine = new THREE.LineLoop(hexagonLine, material)
					let circlePlane = new THREE.Mesh(hexagonPlane, material)
					circleLine.position.copy(position)
					circlePlane.position.copy(position)
					circlePlane.lookAt(new THREE.Vector3(0, 0, 0))
					circleLine.lookAt(new THREE.Vector3(0, 0, 0))

					hexagon.add(circleLine)
					hexagon.add(circlePlane)
					scene.add(hexagon);
				}

				// 初始球體
				function initSphere(x, y, z) {
					var geometry = new THREE.SphereGeometry(1, 100, 100); //球體幾何
					var material = new THREE.MeshBasicMaterial({
						color: 0xffff00
					}); //網格基礎材料

					var sphere = new THREE.Mesh(geometry, material);
					sphere.position.x = x;
					sphere.position.y = y;
					sphere.position.z = z;
					scene.add(sphere);
				}

				function renderScene() {
					orbit.update();
					// 使用requestAnimationFrame函數進行渲染
					requestAnimationFrame(renderScene);
					renderer.render(scene, camera);
				}

				// 渲染的場景
				renderer.render(scene, camera);
			}
			window.onload = init;

			// 随着窗體的變化修改場景
			function onResize() {
				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();
				renderer.setSize(window.innerWidth, window.innerHeight);
			}
			// 監聽窗體調整大小事件
			window.addEventListener('resize', onResize, false);
		</script>
	</body>
</html>
           

 需要完整代碼請留言或者聯系我微信:1171053128

繼續閱讀