3D瓦片
使用
通过 vjmap3d.loadPlugin3dtiles 加载3dtiles插件库
或 通过 vjmap3d.Plugins.loadPlugin("3dtiles") 加载
let tiles3d = await vjmap3d.loadPlugin3dtiles()
let offsetParent = new THREE.Group();
offsetParent.scale.set(0.1, 0.1, 0.1)
app.scene.add( offsetParent );
const url = 'https://vjmap.com/map3d/resources/3dtiles/demo/tileset.json';
let tiles = new tiles3d.TilesRenderer( url );
tiles.manager.addHandler(/\.gltf$/, vjmap3d.LoadManager.gltfLoader);
tiles.manager.addHandler(/\.drc$/, vjmap3d.LoadManager.dracoLoader);
tiles.errorTarget = 2;
offsetParent.add( tiles.group );
app.signal.onAppAfterUpdate.add(() => {
tiles.setCamera( app.camera );
tiles.setResolutionFromRenderer( app.camera, app.renderer );
tiles.update()
})
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
scene: {
gridHelper: { visible: true } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
},
control: {
initState: {
cameraTarget: new THREE.Vector3(24, 0, 8),
cameraPosition: new THREE.Vector3(32, 28, 24),
}
}
})
let tiles3d = await vjmap3d.loadPlugin3dtiles()
let offsetParent = new THREE.Group();
offsetParent.scale.set(0.1, 0.1, 0.1)
app.scene.add( offsetParent );
const url = 'https://vjmap.com/map3d/resources/3dtiles/demo/tileset.json';
let tiles = new tiles3d.TilesRenderer( url );
tiles.manager.addHandler(/\.gltf$/, vjmap3d.LoadManager.gltfLoader);
tiles.manager.addHandler(/\.drc$/, vjmap3d.LoadManager.dracoLoader);
tiles.errorTarget = 2;
offsetParent.add( tiles.group );
app.signal.onAppAfterUpdate.add(() => {
tiles.setCamera( app.camera );
tiles.setResolutionFromRenderer( app.camera, app.renderer );
tiles.update()
})
自动叠加互联网地图(3D模式)
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
scene: {
gridHelper: { visible: false } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
},
control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
let provider = new vjmap3d.MapProvider(
[
{
url: "https://t2.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK"
}
],
{
rootTile: [10, 834, 444],
}
);
let mapview = new vjmap3d.MapView({
provider,
baseScale: 5000
});
app.scene.add(mapview);
//
let tiles3d = await vjmap3d.loadPlugin3dtiles();
//
let url = 'https://vjmap.com/map3d/resources/3dtiles/baowei/tileset.json';
let meta = await vjmap3d.httpHelper.get(url);
let data = meta.data;
let box = data.root.boundingVolume.box;
let lngLat = tiles3d.get3dTilesLngLatHeight(box.slice(0, 3), data.root.transform); // box 前 3 个数是中心点;直接传整个 box 得到的不是真实中心
lngLat[2] = 0;// 不考虑高度
//
let worldPoint = mapview.lngLatToWorld(lngLat);
let marker2d = new vjmap3d.Marker2D({
color: vjmap3d.render2d.color.random()
});
marker2d.setPosition(worldPoint);
marker2d.addTo(app);
//
const tiles = new tiles3d.TilesRenderer(url, {
clearRootTransform: true
});
tiles.manager.addHandler(/\.gltf$/, vjmap3d.LoadManager.gltfLoader);
tiles.manager.addHandler(/\.drc$/, vjmap3d.LoadManager.dracoLoader);
tiles.errorTarget = 2;
tiles.onLoadModel = (scene) => {
console.log(" --- scene --- ")
}
let offsetParent = new THREE.Group();
offsetParent.add(tiles.group)
//
let ent = vjmap3d.Entity.fromObject3d(offsetParent);
ent.position.copy(worldPoint);
let scale = 1 / mapview.worldResolution()
ent.scale.set(scale, scale, scale);
ent.rotation.set(-Math.PI / 2, 0, 0);
ent.position.y += 0.5;
ent.addTo(app);
//
app.signal.onAppAfterUpdate.add(() => {
tiles.setCamera(app.camera);
tiles.setResolutionFromRenderer(app.camera, app.renderer);
tiles.update();
});
app.cameraControl.moveTo( worldPoint.x, worldPoint.y, worldPoint.z, false)
app.cameraControl.zoom(9, false)
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
scene: {
gridHelper: { visible: false } // 是否显示坐标网格
},
camera: { // 相机设置
viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
},
control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
let provider = new vjmap3d.MapProvider(
[
{
url: "https://t2.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK"
}
],
{
rootTile: [10, 834, 444],
}
);
let mapview = new vjmap3d.MapView({
provider,
baseScale: 5000
});
app.scene.add(mapview);
//
let tiles3d = await vjmap3d.loadPlugin3dtiles();
//
let url = 'https://vjmap.com/map3d/resources/3dtiles/baowei/tileset.json';
let meta = await vjmap3d.httpHelper.get(url);
let data = meta.data;
let box = data.root.boundingVolume.box;
let lngLat = tiles3d.get3dTilesLngLatHeight(box.slice(0, 3), data.root.transform); // box 前 3 个数是中心点;直接传整个 box 得到的不是真实中心
lngLat[2] = 0;// 不考虑高度
//
let worldPoint = mapview.lngLatToWorld(lngLat);
let marker2d = new vjmap3d.Marker2D({
color: vjmap3d.render2d.color.random()
});
marker2d.setPosition(worldPoint);
marker2d.addTo(app);
//
const tiles = new tiles3d.TilesRenderer(url, {
clearRootTransform: true
});
tiles.manager.addHandler(/\.gltf$/, vjmap3d.LoadManager.gltfLoader);
tiles.manager.addHandler(/\.drc$/, vjmap3d.LoadManager.dracoLoader);
tiles.errorTarget = 2;
tiles.onLoadModel = (scene) => {
console.log(" --- scene --- ")
}
let offsetParent = new THREE.Group();
offsetParent.add(tiles.group)
//
let ent = vjmap3d.Entity.fromObject3d(offsetParent);
ent.position.copy(worldPoint);
let scale = 1 / mapview.worldResolution()
ent.scale.set(scale, scale, scale);
ent.rotation.set(-Math.PI / 2, 0, 0);
ent.position.y += 0.5;
ent.addTo(app);
//
app.signal.onAppAfterUpdate.add(() => {
tiles.setCamera(app.camera);
tiles.setResolutionFromRenderer(app.camera, app.renderer);
tiles.update();
});
app.cameraControl.moveTo( worldPoint.x, worldPoint.y, worldPoint.z, false)
app.cameraControl.zoom(9, false)
自动叠加互联网地图(3D做为唯杰地图的一个图层展示)
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 根据地图范围建立经纬度投影坐标系
let prj = new vjmap.LnglatProjection();
// 地图对象
let map = new vjmap.Map({
container: 'map', // DIV容器ID
style: {
version: svc.styleVersion(),
glyphs: svc.glyphsUrl(),
sources: {
sat: {
type: 'raster',
tileSize: 256, // 天地图是 256 瓦片
tiles: ["https://t3.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK"],
}
},
layers: [{
id: 'sat',
type: 'raster',
source: 'sat'
}]
},
center: prj.toLngLat([116.3912, 39.9073]),
zoom: 10,
pitch: 0,
antialias: true, // 反锯齿
renderWorldCopies: false // 不显示多屏地图
});
// 关联服务对象和投影对象
map.attach(svc, prj);
// 根据地图本身范围缩放地图至全图显示
//map.fitMapBounds();
await map.onLoad();
//
// 创建3d图层
let mapLayer = new vjmap3d.MapThreeLayer(map, {
stat: {
show: true,
left: "0"
}
});
map.addLayer(new vjmap.ThreeLayer({ context: mapLayer }))
let app = mapLayer.app;
// 注意:app.map 是 MapThreeLayer 运行时挂上的 2D 地图实例(仅地图集成模式存在;纯 3D 场景没有)
//
let tiles3d = await vjmap3d.loadPlugin3dtiles();
//
let url = 'https://vjmap.com/map3d/resources/3dtiles/baowei/tileset.json';
let meta = await vjmap3d.httpHelper.get(url);
let data = meta.data;
let box = data.root.boundingVolume.box;
let lngLat = tiles3d.get3dTilesLngLatHeight(box.slice(0, 3), data.root.transform); // box 前 3 个数是中心点;直接传整个 box 得到的不是真实中心
lngLat[2] = 0;// 不考虑高度
app.map.setCenter(lngLat);
app.map.setZoom(17);
let marker = new vjmap.Marker();
marker.setLngLat(lngLat);
marker.addTo(app.map)
const tiles = new tiles3d.TilesRenderer(url, {
clearRootTransform: true
});
tiles.manager.addHandler(/\.gltf$/, vjmap3d.LoadManager.gltfLoader);
tiles.manager.addHandler(/\.drc$/, vjmap3d.LoadManager.dracoLoader);
tiles.errorTarget = 2;
tiles.onLoadModel = (scene) => {
//console.log(" --- scene --- ")
}
let offsetParent = new THREE.Group();
offsetParent.add(tiles.group)
//
let ent = vjmap3d.Entity.fromObject3d(offsetParent);
ent.position.copy(vjmap3d.map2dUtils.projectToWorld(lngLat));
let scale = 1.0 / vjmap3d.map2dUtils.WorldResolution
ent.scale.set(scale, scale, scale);
ent.addTo(app);
app.signal.onAppAfterUpdate.add(() => {
tiles.setCamera(app.camera);
tiles.setResolutionFromRenderer(app.camera, app.renderer);
tiles.update();
});
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 根据地图范围建立经纬度投影坐标系
let prj = new vjmap.LnglatProjection();
// 地图对象
let map = new vjmap.Map({
container: 'map', // DIV容器ID
style: {
version: svc.styleVersion(),
glyphs: svc.glyphsUrl(),
sources: {
sat: {
type: 'raster',
tileSize: 256, // 天地图是 256 瓦片
tiles: ["https://t3.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK"],
}
},
layers: [{
id: 'sat',
type: 'raster',
source: 'sat'
}]
},
center: prj.toLngLat([116.3912, 39.9073]),
zoom: 10,
pitch: 0,
antialias: true, // 反锯齿
renderWorldCopies: false // 不显示多屏地图
});
// 关联服务对象和投影对象
map.attach(svc, prj);
// 根据地图本身范围缩放地图至全图显示
//map.fitMapBounds();
await map.onLoad();
//
// 创建3d图层
let mapLayer = new vjmap3d.MapThreeLayer(map, {
stat: {
show: true,
left: "0"
}
});
map.addLayer(new vjmap.ThreeLayer({ context: mapLayer }))
let app = mapLayer.app;
// 注意:app.map 是 MapThreeLayer 运行时挂上的 2D 地图实例(仅地图集成模式存在;纯 3D 场景没有)
//
let tiles3d = await vjmap3d.loadPlugin3dtiles();
//
let url = 'https://vjmap.com/map3d/resources/3dtiles/baowei/tileset.json';
let meta = await vjmap3d.httpHelper.get(url);
let data = meta.data;
let box = data.root.boundingVolume.box;
let lngLat = tiles3d.get3dTilesLngLatHeight(box.slice(0, 3), data.root.transform); // box 前 3 个数是中心点;直接传整个 box 得到的不是真实中心
lngLat[2] = 0;// 不考虑高度
app.map.setCenter(lngLat);
app.map.setZoom(17);
let marker = new vjmap.Marker();
marker.setLngLat(lngLat);
marker.addTo(app.map)
const tiles = new tiles3d.TilesRenderer(url, {
clearRootTransform: true
});
tiles.manager.addHandler(/\.gltf$/, vjmap3d.LoadManager.gltfLoader);
tiles.manager.addHandler(/\.drc$/, vjmap3d.LoadManager.dracoLoader);
tiles.errorTarget = 2;
tiles.onLoadModel = (scene) => {
//console.log(" --- scene --- ")
}
let offsetParent = new THREE.Group();
offsetParent.add(tiles.group)
//
let ent = vjmap3d.Entity.fromObject3d(offsetParent);
ent.position.copy(vjmap3d.map2dUtils.projectToWorld(lngLat));
let scale = 1.0 / vjmap3d.map2dUtils.WorldResolution
ent.scale.set(scale, scale, scale);
ent.addTo(app);
app.signal.onAppAfterUpdate.add(() => {
tiles.setCamera(app.camera);
tiles.setResolutionFromRenderer(app.camera, app.renderer);
tiles.update();
});
类型定义文件
/**
* Documented 3d-tile state managed by the TilesRenderer* / used/usable in priority / traverseFunctions!
*/
declare interface Tile extends TileBase {
parent: Tile;
/**
* Hierarchy Depth from the TileGroup
*/
__depth: number;
/**
* The screen space error for this tile
*/
__error: number;
/**
* How far is this tiles bounds from the nearest active Camera.
* Expected to be filled in during calculateError implementations.
*/
__distanceFromCamera: number;
/**
* This tile is currently active if:
* 1: Tile content is loaded and ready to be made visible if needed
*/
__active: boolean;
/**
* This tile is currently visible if:
* 1: Tile content is loaded
* 2: Tile is within a camera frustum
* 3: Tile meets the SSE requirements
*/
__visible: boolean;
/**
* Whether or not the tile was visited during the last update run.
*/
__used: boolean;
/**
* Whether or not the tile was within the frustum on the last update run.
*/
__inFrustum: boolean;
/**
* TODO: Document this if it is useful enough to be the default property in the LRU sorting.
*/
__depthFromRenderedParent: number;
}
/**
* 3d-tiles Tile object per spec:
* (incomplete, expanding as features become supported by this package.)
*
* See spec for full schema: https://github.com/CesiumGS/3d-tiles/blob/master/specification/schema/tile.schema.json
*/
declare interface TileBase {
boundingVolume: {
/**
* An array of 12 numbers that define an oriented bounding box. The first three elements define the x, y, and z
* values for the center of the box. The next three elements (with indices 3, 4, and 5) define the x axis
* direction and half-length. The next three elements (indices 6, 7, and 8) define the y axis direction and
* half-length. The last three elements (indices 9, 10, and 11) define the z axis direction and half-length.
*/
box?: number[];
/**
* An array of four numbers that define a bounding sphere. The first three elements define the x, y, and z
* values for the center of the sphere. The last element (with index 3) defines the radius in meters.
*/
sphere?: number[];
};
/**
* The error, in meters, introduced if this tileset is not rendered. At runtime, the geometric error is used to compute screen space error (SSE), i.e., the error measured in pixels.
*/
geometricError: number;
children?: TileBase[];
content?: {
uri: string;
/**
* Dictionary object with content specific extension objects.
*/
extensions?: Record<string, any>;
extras?: Record<string, any>;
url?: string;
};
/**
* Dictionary object with tile specific extension objects.
*/
extensions?: Record<string, any>;
extras?: Record<string, any>;
refine?: 'REPLACE' | 'ADD';
transform?: number[];
}
declare namespace Tiles3dPluginType {
export {
get3dTilesLngLatHeight,
TileBase,
Tile,
Tileset,
TilesRendererBase,
TilesGroup,
TilesRenderer
}
}
export { Tiles3dPluginType }
/**
* 通过3dtiles的boundingVolume和transform获取经纬度
* @param box 3dtiles里面boundingVolume.box的值,默认取中心点。也可以直接传一个坐标[number,number,number]
* ⚠️ 实现把 12 个数的 box 当成 4 个点(中心+3 个半轴向量)求轴对齐包围盒的中心,一般**不是** box 的真实中心;
* 需要准确位置时请传 box.slice(0, 3)(3D Tiles 的 box 前 3 个数就是中心点)
*/
declare function get3dTilesLngLatHeight(box: number[], matrix?: number[]): [number, number, number];
/**
* A 3d-tiles tileset.
*
* Schema, see: https://github.com/CesiumGS/3d-tiles/blob/main/specification/schema/tileset.schema.json
*/
declare interface Tileset {
/**
* Metadata about the entire tileset.
*/
asset: {
/**
* 3d-tiles version
*/
version: string;
/**
* Application specific version
*/
tilesetVersion?: string;
/**
* Dictionary object with extension-specific objects.
*/
extensions?: Record<string, any>;
};
/**
* The error, in meters, introduced if this tileset is not rendered. At runtime, the geometric error is used to compute screen space error (SSE), i.e., the error measured in pixels.
*/
geometricError: Number;
/**
* The root tile.
*/
root: TileBase;
/**
* Names of 3D Tiles extensions used somewhere in this tileset.
*/
extensionsUsed?: string[];
/**
* Names of 3D Tiles extensions required to properly load this tileset.
*/
extensionsRequired?: string[];
/**
* A dictionary object of metadata about per-feature properties.
*/
properties?: Record<string, any>;
/**
* Dictionary object with extension-specific objects.
*/
extensions?: Record<string, any>;
extras?: Record<string, any>;
}
declare class TilesGroup extends Group_2 {
tilesRenderer: TilesRenderer;
constructor(tilesRenderer: TilesRenderer);
}
declare class TilesRenderer extends TilesRendererBase {
constructor(url: String, options?: {
clearRootTransform?: boolean;
});
autoDisableRendererCulling: Boolean;
optimizeRaycast: Boolean;
manager: LoadingManager;
group: TilesGroup;
getBoundingBox(box: Box3): Boolean;
getOrientedBoundingBox(box: Box3, matrix: Matrix4): Boolean;
getBoundingSphere(sphere: Sphere): Boolean;
hasCamera(camera: Camera): Boolean;
setCamera(camera: Camera): Boolean;
deleteCamera(camera: Camera): Boolean;
setResolution(camera: Camera, x: Number, y: Number): Boolean;
setResolution(camera: Camera, resolution: Vector2): Boolean;
setResolutionFromRenderer(camera: Camera, renderer: WebGLRenderer): Boolean;
forEachLoadedModel(callback: (scene: Object3D, tile: Tile) => void): void;
onLoadTileSet: ((tileSet: Tileset) => void) | null;
onLoadModel: ((scene: Object3D, tile: Tile) => void) | null;
onDisposeModel: ((scene: Object3D, tile: Tile) => void) | null;
onTileVisibilityChange: ((scene: Object3D, tile: Tile, visible: boolean) => void) | null;
addEventListener(type: String, cb: (e: Object) => void);
hasEventListener(type: String, cb: (e: Object) => void);
removeEventListener(type: String, cb: (e: Object) => void);
dispatchEvent(e: Object);
}
declare class TilesRendererBase {
readonly rootTileset: Object | null;
readonly root: Object | null;
errorTarget: Number;
errorThreshold: Number;
loadSiblings: Boolean;
displayActiveTiles: Boolean;
maxDepth: Number;
stopAtEmptyTiles: Boolean;
fetchOptions;
/** function to preprocess the url for each individual tile */
preprocessURL: ((uri: string | URL) => string) | null;
lruCache;
parseQueue;
downloadQueue;
constructor(url: String);
update(): void;
traverse(beforeCb: ((tile: Object, parent: Object, depth: Number) => Boolean) | null, afterCb: ((tile: Object, parent: Object, depth: Number) => Boolean) | null): void;
dispose(): void;
resetFailedTiles(): void;
}