如何用React-map-gl、Deck.gl加载用户上传的IFC/GLTF模型
问题解决:用户上传GLTF/IFC模型在deck.gl中加载失败
一、GLTF模型加载错误解决
错误信息
deck: loading scenegraph of ScenegraphLayer({id: 'scenegraph-layer'}): No valid loader found (0f3e4b8a-f7d4-4cbd-868e-c4c463552bca, MIME type: not provided, first bytes: not available)
问题原因
- ScenegraphLayer默认未显式指定GLTF加载器,Blob URL的MIME类型未被正确识别
- 初始状态下
gltfUrl为undefined,此时创建的ScenegraphLayer会尝试加载空资源触发错误
解决方案
- 安装
@loaders.gl/gltf依赖,提供GLTF专用加载器 - 显式为ScenegraphLayer指定
loader为GLTFLoader - 仅当
gltfUrl存在时才创建ScenegraphLayer,避免无效加载
修改后的代码
import { useState } from 'react'; import { Map } from 'react-map-gl'; import { ScenegraphLayer } from '@deck.gl/mesh-layers'; import { DeckGL } from 'deck.gl'; import maplibreGl from 'maplibre-gl'; import { GLTFLoader } from '@loaders.gl/gltf'; function App() { const [gltfUrl, setGltfUrl] = useState(); const gltfUpload = (e) => { const file = e.target.files[0]; const url = URL.createObjectURL(file); setGltfUrl(url); }; // 仅当gltfUrl存在时创建图层 const layers = gltfUrl ? [new ScenegraphLayer({ id: 'scenegraph-layer', pickable: true, data: [ { name: 'Colma (COLM)', address: '365 D Street, Colma CA 94014', exits: 4214, coordinates: [148.9819, -35.39847], }, ], scenegraph: gltfUrl, loader: GLTFLoader, // 显式指定GLTF加载器 getPosition: (d) => d.coordinates, getOrientation: (d) => [0, Math.random() * 180, 90], sizeScale: 1, _lighting: 'pbr', })] : []; return ( <> <input type='file' onChange={gltfUpload} style={{ zIndex: 10, position: 'fixed' }} accept=".gltf,.glb" // 限制文件类型,避免错误上传 /> <DeckGL initialViewState={{ longitude: 148.9819, latitude: -35.39847, zoom: 18, pitch: 60, }} controller={true} layers={layers} > <Map mapLib={maplibreGl} mapStyle='https://api.maptiler.com/maps/basic/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL' /> </DeckGL> </> ); } export default App;
二、IFC模型加载错误解决
错误信息
Uncaught (in promise) Error: Cannot convert supplied data type at getArrayBufferOrStringFromData
问题原因
Tile3DLayer+I3SLoader用于加载I3S格式的3D瓦片数据集,无法直接加载web-ifc-three输出的Three.js Object3D对象- IFC模型加载后需要转换为deck.gl可识别的场景图格式,而非直接传入Tile3DLayer
解决方案
- 使用web-ifc-three加载IFC得到Three.js Object3D
- 将加载后的Object3D传入ScenegraphLayer作为
scenegraph(deck.gl支持直接传入Three.js对象) - 调整图层配置,适配IFC模型的位置与缩放
修改后的代码
import { useState, useRef } from 'react'; import maplibregl from 'maplibre-gl'; import DeckGL from '@deck.gl/react'; import { Map } from 'react-map-gl'; import { ScenegraphLayer } from '@deck.gl/mesh-layers'; import { IFCLoader } from 'web-ifc-three'; function App() { const [ifcModel, setIfcModel] = useState(); const ifcLoaderRef = useRef(new IFCLoader()); const ifcUpload = async (e) => { const file = e.target.files[0]; const url = URL.createObjectURL(file); try { const model = await ifcLoaderRef.current.loadAsync(url); // 可选:调整模型缩放,默认IFC模型可能尺寸过大 model.scale.set(0.01, 0.01, 0.01); setIfcModel(model); } catch (err) { console.error('IFC加载失败:', err); } }; // 仅当ifcModel存在时创建图层 const layers = ifcModel ? [new ScenegraphLayer({ id: 'ifc-scenegraph-layer', pickable: true, data: [ { coordinates: [148.9819, -35.39847], }, ], scenegraph: ifcModel, // 直接传入Three.js Object3D getPosition: (d) => d.coordinates, getOrientation: () => [0, 0, 0], sizeScale: 1, _lighting: 'pbr', })] : []; return ( <> <input type='file' onChange={ifcUpload} style={{ zIndex: 10, position: 'fixed' }} accept=".ifc" // 限制文件类型 /> <DeckGL initialViewState={{ longitude: 148.9819, latitude: -35.39847, zoom: 18, pitch: 60, }} controller={true} layers={layers} > <Map mapLib={maplibregl} mapStyle='https://api.maptiler.com/maps/basic/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL' /> </DeckGL> </> ); }
内容的提问来源于stack exchange,提问作者Hillzkred
相关产品推荐
相关产品推荐

