three-tile
一个基于 Three.js 开发的轻量级三维瓦片地图库,专为 Three.js 应用程序提供高性能的地图渲染能力。
npm i three-tile -S
# 或
yarn add three-tile -S
>=0.171.0<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta />
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
<title>three-tile最小化应用</title>
</head>
<style>
html,
body {
background-color: #333;
height: 100%;
width: 100%;
padding: 0;
margin: 0;
display: flex;
overflow: hidden;
}
#map {
height: 100%;
width: 100%;
}
</style>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.171.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.171.0/examples/jsm/",
"three-tile": "https://unpkg.com/three-tile@0.11.10/dist"
}
}
</script>
<body>
<div id="map"></div>
<script type="module">
import * as THREE from "three";
import * as tt from "three-tile";
import { MapControls } from "three/addons/controls/MapControls.js";
console.log(`three-tile v${tt.version} start!`);
// 创建场景
const createViewer = container => {
const width = container.clientWidth;
const height = container.clientHeight;
// scene
const scene = new THREE.Scene();
// renderer
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
// camera
const camera = new THREE.PerspectiveCamera(60, width / height, 10, 4e7);
camera.position.set(0, camera.far / 2, 0);
// ambient light
const ambLight = new THREE.AmbientLight(0xffffff);
scene.add(ambLight);
// directional light
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(0, 5e6, 1e5);
dirLight.target.position.set(0, 0, -5e6);
scene.add(dirLight);
// controls
const controls = new MapControls(camera, renderer.domElement);
controls.maxDistance = 2e7;
controls.minDistance = 10;
controls.enableDamping = true;
// add renderer to container
container.appendChild(renderer.domElement);
return {
scene,
camera,
renderer,
controls,
ambLight,
dirLight,
};
};
const createMap = () => {
// 创建影像数据源
const imgSource = new tt.TileSource({
url: "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
dataType: "image",
minLevel: 0,
maxLevel: 18,
});
// 创建地形数据源(可选)
const demSource = new tt.TileSource({
url: "https://server.arcgisonline.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer/tile/{z}/{y}/{x}",
dataType: "lerc",
minLevel: 5,
maxLevel: 13,
});
// 创建地图
const map = tt.TileMap.create({
// 影像数据源
imgSource: imgSource,
// 地形数据源
demSource: demSource,
lon0: 90,
});
return map;
};
// 初始化场景
const viewer = createViewer(document.querySelector("#map"));
// 创建地图
const map = createMap();
// 地图旋转到xz平面
map.rotateX(-Math.PI / 2);
// 地图添加到场景
viewer.scene.add(map);
// 动画循环
viewer.renderer.setAnimationLoop(() => {
viewer.controls.update();
viewer.renderer.render(viewer.scene, viewer.camera);
});
</script>
</body>
</html>
TileMap 是整个库的核心类,继承自 THREE.Object3D,负责瓦片的加载、渲染和管理。
const map = TileMap.create(params: MapParams)
interface MapParams {
// 影像数据源
imgSource: ISource[] | ISource;
// 地形数据源(可选),默认undefined
demSource?: ISource;
// 中央子午线经度,默认0
lon0?: 0 | 90 | -90;
// 最小缩放级别,默认2
minLevel?: number;
// 地图范围 [minLon, minLat, maxLon, maxLat]
bounds?: [number, number, number, number];
// 调试级别 0:关闭, 1+:开启,默认0
debug?: number;
}
数据源定义了瓦片数据的获取方式和元数据信息:
const source = new tt.TileSource(params: SourceOptions );
interface SourceOptions {
/** 数据类型标识,指示用哪个加载器加载,默认为"image" */
dataType?: string;
/** 数据所有者 */
attribution?: string;
/** 瓦片最大级别 */
minLevel?: number;
/** 瓦片最小级别 */
maxLevel?: number;
/** 投影方式,默认3857 */
projectionID?: ProjectionType;
/** 图层显示时的透明度,0-1 */
opacity?: number;
/* 数据经纬度范围 [minLon,minLat,maxLon,maxLat] */
bounds?: [number, number, number, number];
/** 瓦片url模板 */
url?: string;
/** 瓦片url子域 */
subdomains?: string[] | string;
}
| 类/接口 | 描述 | 主要方法 |
|---|---|---|
TileMap |
地图核心类,继承自 THREE.Object3D |
geo2world(), world2geo(), getLocalInfoFromScreen(), addEventListener() |
TileSource |
数据源类,定义瓦片数据来源 | getUrl(), getBounds() |
interface MapParams {
imgSource: ISource[] | ISource; // 影像数据源
demSource?: ISource; // 地形数据源(可选)
lon0?: 0 | 90 | -90; // 中央子午线经度
minLevel?: number; // 最小缩放级别
maxLevel?: number; // 最大缩放级别(废弃,自动计算)
bounds?: [number, number, number, number]; // 地图范围 [minLon, minLat, maxLon, maxLat]
debug?: number; // 调试级别 0:关闭, 1+:开启
}
// 坐标转换
geo2world(geo: Vector3): Vector3; // 地理坐标转世界坐标
world2geo(world: Vector3): Vector3; // 世界坐标转地理坐标
getLocalInfoFromScreen(camera: Camera, pointer: Vector2): LocationInfo | undefined;
getLocalInfoFromGeo(geo: Vector3): LocationInfo | undefined;
getLocalInfoFromWorld(pos: Vector3): LocationInfo | undefined;
// 地图控制
reload(): void; // 重新加载地图
update(): void; // 手动更新地图
addEventListener(type: string, listener: Function): void;
removeEventListener(type: string, listener: Function): void;
interface ISource {
dataType: string; // 数据类型标识 ('image', 'terrain-rgb', 'lerc', 'dem' 等)
url: string; // URL 模板,支持 {x}, {y}, {z} 占位符
attribution: string; // 版权信息
minLevel: number; // 最小级别
maxLevel: number; // 最大级别
projectionID: "3857" | "4326"; // 投影系统
opacity?: number; // 透明度
transparent?: boolean; // 是否透明
isTMS?: boolean; // 是否为 TMS 服务
bounds?: [number, number, number, number]; // 数据范围 [minLon,minLat,maxLon,maxLat]
getUrl(x: number, y: number, z: number): string | undefined;
}
// 地图事件类型
type MapEventTypes = "ready" | "tile-loaded" | "tile-unloaded" | "update";
// 监听地图事件
map.addEventListener("ready", () => {
console.log("地图就绪");
});
map.addEventListener("tile-loaded", event => {
console.log("瓦片加载完成:", event.tile.x, event.tile.y, event.tile.z);
});
map.addEventListener("update", () => {
console.log("地图更新");
});
import {
waitFor, // 等待条件成立
registerImgLoader, // 注册影像加载器
registerDEMLoader, // 注册地形加载器
getImgLoader, // 获取影像加载器
getDEMLoader, // 获取地形加载器
getTileLoaders, // 获取所有加载器
} from "three-tile";
// 等待地图就绪
await waitFor(() => map.isReady);
// 获取特定类型的加载器
const imageLoader = getImgLoader<TileImageLoader>("image");
const terrainLoader = getDEMLoader<TerrainRGBLoader>("terrain-rgb");
// 获取所有加载器信息
const loaders = getTileLoaders();
console.log("影像加载器:", loaders.imgLoaders);
console.log("地形加载器:", loaders.demLoaders);
interface LocationInfo extends Intersection {
location: Vector3; // 经纬度信息 (经度, 纬度, 高度米)
// 继承自 Intersection:
// - distance: 射线投射原点和相交部分之间的距离
// - point: 相交部分的点(世界坐标)
// - face: 相交的面
// - faceIndex: 相交的面的索引
// - object: 相交的物体
// - uv: 相交部分的点的 UV 坐标
// - normal: 交点处的内插法向量
}
感谢以下开源项目的启发和支持:
注意: 本库不含任何地图数据,示例中使用的地图数据均为第三方服务,使用时请遵守相关法律法规和版权要求。
Made with ❤️ by Guo Jiangfeng