three-tile icon indicating copy to clipboard operation
three-tile copied to clipboard

【插件】增加了单个 tif dem 地形数据 loader

Open 497983606 opened this issue 8 months ago • 1 comments

发现 fork 提不上来,补充在这里,给用需要的人(用于局部地形加载)

需要先安装两个库 geotiff 用以解析 tif dem数据,geotiff-tile 这个库是前者的补丁,用以处理经纬度范围裁剪数据,这个库的 ts 类型声明有 bug,有需要的可以自己下载重新编译,也可以 @ts-ignore

plugin
|-singleTifDEMLoader
|--index.ts
|--TifDEMLoader.ts
|--TifDEMSource.ts

index.ts

export * from "./TifDEMLoader";
export * from "./TifDEMSource";

import { registerDEMLoader } from "../..";
import { TifDEMLoder } from "./TifDEMLoader";

registerDEMLoader(new TifDEMLoder());

TifDEMSource.ts

import { TileSource } from "../..";
export class TifDemSource extends TileSource {
	public dataType = "tif-dem";
	public data?: any;
        public author= "c3d"
}

TifDEMLoader.ts

import { BufferGeometry } from "three";
import { ITileGeometryLoader, TileSourceLoadParamsType, ISource, TileGeometry, ITileLoaderInfo} from '../..'
import { fromUrl } from 'geotiff'
// @ts-ignore 由于 geotiff-tile 包的类型声明问题,暂时忽略类型检查
import createTile from "geotiff-tile";
const EarthRad = 6378.137;
export class TifDEMLoder implements ITileGeometryLoader {
	public dataType: string = "tif-dem";
	public name: string = "tif loader";
	public author?: string = 'chaoxl';
	public lon0: number = 0;
	public info: ITileLoaderInfo = { version: '0.0.1' };
	public mapWidth = 2 * Math.PI * EarthRad; //E-W scacle Earth's circumference(km)
	public mapHeight = this.mapWidth; //S-N scacle Earth's circumference(km)
	public mapDepth = 1; //Height scale
	public tiffData: any = null;
	async load(params: TileSourceLoadParamsType<ISource & { url: string }>): Promise<BufferGeometry> {
		const { source: _source, x: _x, y: _y, z: _z,} = params;
		if( !this.tiffData ) await this.getTiffData(_source.url)
		const tilebounds = this.getLonLatBoundsFromXYZ(_x, _y, _z);
		const tileOpts: any = {
			geotiff: this.tiffData,
			bbox: tilebounds,
			method: "bilinear",
			tile_height: 20,
			tile_width: 20
		};
		const { tile }  = await createTile(tileOpts);
		const geometry = new TileGeometry()
		geometry.setData(new Float32Array( tile[0].map((i: any) => i ? i : null ) ))
		return geometry
	}

	public async getTiffData(url: string) {
		this.tiffData = await fromUrl(url)
	}
	
	public getProjBoundsFromXYZ(x: number, y: number, z: number): [number, number, number, number] {
		const p1 = this.getTileXYZproj(x, y, z);
		const p2 = this.getTileXYZproj(x + 1, y + 1, z);
		return [Math.min(p1.x, p2.x), Math.min(p1.y, p2.y), Math.max(p1.x, p2.x), Math.max(p1.y, p2.y)];
	}

	public getLonLatBoundsFromXYZ(x: number, y: number, z: number): [number, number, number, number] {
		const projectBounds = this.getProjBoundsFromXYZ(x, y, z);
		const p1 = this.unProject(projectBounds[0], projectBounds[1]);
		const p2 = this.unProject(projectBounds[2], projectBounds[3]);
		return [p1.lon, p1.lat, p2.lon, p2.lat];
	}

	public unProject(x: number, y: number) {
		let lon = (x / EarthRad) * (180 / Math.PI) + this.lon0; // 考虑中心经度偏移
		if (lon > 180) lon -= 360;
		const latRad = 2 * Math.atan(Math.exp(y / EarthRad)) - Math.PI / 2;
		const lat = latRad * (180 / Math.PI);
		return { lat, lon };
	}

	public getTileXYZproj(x: number, y: number, z: number): { x: number, y: number } {
		const w = this.mapWidth;
		const h = this.mapHeight / 2;
		const px = (x / Math.pow(2, z)) * w - w / 2;
		const py = h - (y / Math.pow(2, z)) * h * 2;
		return { x: px, y: py };
	}
}

497983606 avatar Apr 15 '25 03:04 497983606

非常好,下一版合并进项目!!!

sxguojf avatar Apr 15 '25 03:04 sxguojf