
簡介面向使用Cesium搭建三維地球、又需要在斷網(wǎng)環(huán)境展示地形起伏的開發(fā)者這份資料把DEM高程tif轉(zhuǎn)為Cesium可讀離線terrain瓦片的整套流程打包在一起省去分別尋找Python、GDAL等舊版依賴的麻煩。壓縮包共11個(gè)文件約45.9MB以msi與exe安裝程序、py腳本、pdf說明為主另附j(luò)son配置、terrain樣例與txt提示兼顧環(huán)境搭建與轉(zhuǎn)換實(shí)操。已有3095人學(xué)習(xí)下載說明該離線地形方案在實(shí)際項(xiàng)目中需求穩(wěn)定。其中安裝包覆蓋Python 2.7、GDAL、PIL、NumPy等地理處理基礎(chǔ)組件兩個(gè)腳本分別承擔(dān)坐標(biāo)配準(zhǔn)與DEM切片示例DEM數(shù)據(jù)和生成后的tiles結(jié)果可用于對(duì)照驗(yàn)證layer.json便于接入Cesium地形提供者配套PDF與說明文本記錄步驟、參數(shù)與常見報(bào)錯(cuò)處理適合希望一次跑通鏈路并自行調(diào)優(yōu)切片參數(shù)的中級(jí)開發(fā)者。1. 從一份 DEM 到能離線加載的地形Cesium terrain 切片到底在切什么Cesium生成離線terrain地形數(shù)據(jù)的核心不是把 GeoTIFF 改個(gè)后綴而是把高程?hào)鸥裰夭蓸映梢活w顆帶索引的 quantized-mesh 瓦片再配一份 layer.json 告訴瀏覽器“哪一級(jí)、哪一塊、去哪拿”。我見過不少園區(qū)三維項(xiàng)目底圖能離線模型能離線一到地形就露白根子多在切片網(wǎng)格和 NoData 沒對(duì)齊。這套程序適合做內(nèi)網(wǎng)三維地球、離線指揮大屏、無外網(wǎng)環(huán)境地形底座的團(tuán)隊(duì)新手可以按步驟跑通熟手重點(diǎn)看參數(shù)和驗(yàn)收。它解決的是“沒有在線地形服務(wù)時(shí)Cesium 仍能按 LOD 加載本地地形”的問題。2. DEM 預(yù)處理把投影、NoData 和分辨率一次講透2.1 為什么不能直接拿原始 DEM 去切片很多翻車現(xiàn)場從第一步就埋下了。手里拿到的 DEM 可能是 UTM 投影、地方坐標(biāo)系或者帶偏移的經(jīng)緯度柵格而 Cesium 地形瓦片默認(rèn)按 WGS84 地理坐標(biāo)組織瓦片行列號(hào)也有一套固定規(guī)則。源數(shù)據(jù)坐標(biāo)系不統(tǒng)一切片器會(huì)按錯(cuò)誤的范圍算瓦片索引最后在 Cesium 里看到的地形要么整體偏移要么邊緣被裁掉一塊。另一種常見情況是 NoData 值沒處理原始 DEM 用 -9999 表示空洞切片器把它當(dāng)成真實(shí)高程生成出來的瓦片就會(huì)出現(xiàn)巨大尖刺或黑坑。還有人把 30 米分辨率的 DEM 直接切到 18 級(jí)瓦片數(shù)量爆炸瀏覽器加載到高層級(jí)就卡死這種問題不是 Cesium 的鍋是層級(jí)上限沒跟數(shù)據(jù)分辨率對(duì)齊。我一般會(huì)先把“源數(shù)據(jù)能不能用”這件事拆成三個(gè)檢查項(xiàng)坐標(biāo)系是不是 WGS84、NoData 值是否明確、像元大小對(duì)應(yīng)到 Cesium 的哪一級(jí)。三項(xiàng)里有一項(xiàng)含糊后面切出來的地形都像開盲盒。常見做法是先用gdalinfo看元數(shù)據(jù)再用gdalwarp統(tǒng)一投影和 NoData最后用腳本抽查邊界值。不要急著上切片命令預(yù)處理省下來的十分鐘往往能省掉后面兩小時(shí)的排查。2.2 用 gdalwarp 統(tǒng)一到 WGS84 與目標(biāo)瓦片網(wǎng)格下面這條命令是我處理大多數(shù) DEM 的起點(diǎn)。它把源數(shù)據(jù)重投影到 WGS84統(tǒng)一 NoData并寫成帶分塊的 GeoTIFF。分塊和壓縮不是玄學(xué)切片器讀大文件時(shí)分塊柵格比條帶柵格少很多隨機(jī) I/O尤其是幾十 GB 的 DEM差距很明顯。# 查看源數(shù)據(jù)基本信息坐標(biāo)系、尺寸、NoData、像元大小 gdalinfo -stats ./raw_dem.tif # 重投影到 WGS84統(tǒng)一 NoData輸出帶分塊的 GeoTIFF gdalwarp -overwrite \ -t_srs EPSG:4326 \ -r bilinear \ -srcnodata -9999 \ -dstnodata -32768 \ -of GTiff \ -co TILEDYES \ -co COMPRESSLZW \ -co BIGTIFFIF_SAFER \ ./raw_dem.tif ./dem_wgs84.tif-t_srs EPSG:4326是目標(biāo)坐標(biāo)系Cesium 地形瓦片按經(jīng)緯度組織這一步不要省。-r bilinear是重采樣方法常規(guī)地形用雙線性比較穩(wěn)陡峭山區(qū)可以試cubic但不要用最近鄰最近鄰會(huì)把階地和高程突變放大成臺(tái)階。-srcnodata必須跟源數(shù)據(jù)真實(shí) NoData 一致源數(shù)據(jù)沒有 NoData 時(shí)不要硬寫否則會(huì)把正常高程當(dāng)空洞裁掉。-dstnodata -32768是給后續(xù)切片器一個(gè)明確的無效值標(biāo)記具體值不是重點(diǎn)重點(diǎn)是“源和目標(biāo)要一致”。-co TILEDYES讓輸出變成分塊柵格COMPRESSLZW是無損壓縮BIGTIFFIF_SAFER是給超大文件留后路。如果源數(shù)據(jù)已經(jīng)是 WGS84只是范圍或分辨率不合適可以把-t_srs保留再加-tr指定目標(biāo)像元大小。比如要統(tǒng)一到約 1 弧秒可以寫-tr 0.0002777778 0.0002777778。但不要為了“看起來整齊”強(qiáng)行重采樣到高于源數(shù)據(jù)精度的分辨率那只是把 30 米數(shù)據(jù)插值成 10 米文件變大信息量沒增加Cesium 加載時(shí)還會(huì)多讀一堆無意義的瓦片。2.3 檢查 NoData、邊界和分辨率的三條命令預(yù)處理做完不等于能切。我習(xí)慣用三條檢查把問題攔在切片之前元數(shù)據(jù)、邊界采樣、統(tǒng)計(jì)分布。元數(shù)據(jù)看坐標(biāo)系和 NoData 有沒有寫進(jìn)去邊界采樣看四角是不是異常值統(tǒng)計(jì)分布看最小最大值有沒有離譜。下面這段 Python 用 rasterio 讀一遍輸出比肉眼翻元數(shù)據(jù)更直觀。import rasterio import numpy as np with rasterio.open(dem_wgs84.tif) as ds: print(CRS:, ds.crs) print(SIZE:, ds.width, ds.height) print(NODATA:, ds.nodata) print(TRANSFORM:, ds.transform) # 讀取第一波段maskedTrue 會(huì)把 NoData 標(biāo)成掩膜 arr ds.read(1, maskedTrue) print(MIN/MAX:, arr.min(), arr.max()) print(NODATA_COUNT:, arr.mask.sum()) # 抽查左上、右上、左下、右下四個(gè)角 h, w arr.shape corners [arr[0, 0], arr[0, w - 1], arr[h - 1, 0], arr[h - 1, w - 1]] print(CORNERS:, corners)ds.crs應(yīng)該是EPSG:4326不是的話說明gdalwarp沒生效或者讀錯(cuò)了文件。ds.nodata要和命令里的-dstnodata對(duì)上讀出來是None就要回頭檢查。arr.mask.sum()是無效像元數(shù)量太大不一定是壞事說明邊界裁切正常太小則要懷疑 NoData 沒寫進(jìn)去。四個(gè)角的值最容易被忽略如果角上出現(xiàn) -32768 或異常大值切片后就是邊緣黑塊。統(tǒng)計(jì)最小最大值時(shí)不要只看全圖最好按區(qū)域抽查尤其是跨帶或跨山地的 DEM。2.4 在 Cesium 里看到的“地形空洞”多半是這一步留下的地形空洞在 Cesium 里通常表現(xiàn)成兩種一種是黑色或透明的破洞另一種是突然凹下去的平底坑。前者多是 NoData 沒被切片器識(shí)別后者是把 NoData 填成了 0 或某個(gè)固定高程。血淚經(jīng)驗(yàn)是不要用“填 0”解決空洞。0 在海拔里是海平面山區(qū)里填 0 會(huì)造出一塊平地視覺上比空洞更難受。正確做法是讓切片器識(shí)別無效值并跳過該區(qū)域或者在預(yù)處理階段用鄰域插值補(bǔ)洞但補(bǔ)洞只適合小范圍數(shù)據(jù)缺失大面積缺失應(yīng)該回到數(shù)據(jù)源。還有一種“假空洞”是投影邊緣造成的。源 DEM 范圍是矩形重投影到經(jīng)緯度后四角會(huì)變成弧形邊界邊緣像元被重采樣成 NoData。如果直接切片邊緣會(huì)出現(xiàn)鋸齒狀缺口。我一般會(huì)在gdalwarp時(shí)留一點(diǎn)緩沖或者用gdal_fillnodata.py只處理邊緣一圈不要全圖填。填完再檢查一遍統(tǒng)計(jì)值確認(rèn)沒有把大范圍區(qū)域抹平。這個(gè)步驟沒有后悔藥切片完成后再發(fā)現(xiàn)空洞只能重新切瓦片數(shù)量多的時(shí)候很折磨人。3. 地形切片從 GeoTIFF 到 quantized-mesh 的可執(zhí)行流程3.1 切片工具選型為什么我優(yōu)先選基于 GDAL 的 ctb 路線Cesium 離線地形切片常見有三類路線基于 GDAL 的柵格切片、基于 TIN 簡化的網(wǎng)格生成、以及自己寫腳本把高程?hào)鸥褶D(zhuǎn)成 quantized-mesh。第一類最適合大多數(shù)項(xiàng)目原因是它直接吃 GeoTIFF能生成 layer.json支持頂點(diǎn)法線和水面掩膜命令行也容易塞進(jìn)自動(dòng)化流水線。第二類在地形簡化上更靈活但輸入數(shù)據(jù)往往要先轉(zhuǎn)成點(diǎn)云或 TIN流程更長。第三類適合有明確定制需求的團(tuán)隊(duì)否則維護(hù)成本太高。我優(yōu)先選ctb-tile這條路線不是因?yàn)樗昝蓝且驗(yàn)樗倪吔缜宄斎胧翘幚砗玫?WGS84 DEM輸出是z/x/y.terrain加layer.json參數(shù)不多出錯(cuò)時(shí)能對(duì)著日志查。它的缺點(diǎn)也要提前知道切片過程對(duì)內(nèi)存和磁盤 I/O 比較敏感超大范圍數(shù)據(jù)要分塊切增量更新不如數(shù)據(jù)庫方案靈活水面掩膜需要額外數(shù)據(jù)不是開了參數(shù)就自動(dòng)有水。選型時(shí)先把這些邊界寫進(jìn)項(xiàng)目計(jì)劃比切到一半再換工具強(qiáng)。3.2 一條命令生成 .terrain 瓦片與 layer.json下面這條命令是我常用的基礎(chǔ)版本。它把dem_wgs84.tif切成 0 到 14 級(jí)的地形瓦片輸出到terrain_tiles同時(shí)生成 layer.json并寫入頂點(diǎn)法線。第一次跑建議先用小范圍 DEM 驗(yàn)證不要拿全省數(shù)據(jù)直接開刀。# 創(chuàng)建輸出目錄 mkdir -p ./terrain_tiles # 切地形瓦片0-14 級(jí)雙線性重采樣生成 layer.json寫入頂點(diǎn)法線 ctb-tile \ -o ./terrain_tiles \ -l terrain \ -f terrain \ -c \ -N \ -s 0 \ -e 14 \ -r bilinear \ -t 8 \ ./dem_wgs84.tif-o是輸出目錄目錄必須可寫磁盤空間要提前算。-l terrain是圖層名會(huì)影響 layer.json 里的tiles模板和部分元數(shù)據(jù)不是隨便填也無所謂。-f terrain指定輸出 quantized-mesh 地形格式不要寫成普通高程瓦片格式。-c生成 layer.json缺了它 Cesium 不知道瓦片規(guī)則加載時(shí)直接報(bào)錯(cuò)。-N寫入頂點(diǎn)法線開啟后光照更自然但瓦片體積會(huì)增大內(nèi)網(wǎng)帶寬足夠時(shí)建議開。-s 0和-e 14是起止層級(jí)起始層級(jí)一般從 0 開始結(jié)束層級(jí)要跟 DEM 分辨率匹配。-r bilinear是重采樣方法和預(yù)處理保持一致。-t 8是線程數(shù)按 CPU 核數(shù)一半起步太高會(huì)搶內(nèi)存反而變慢。命令跑完后先別急著開 Cesium。先看輸出目錄里有沒有l(wèi)ayer.json再看0/、1/這些層級(jí)目錄是否生成。如果layer.json有但瓦片目錄是空的多半是輸入范圍或?qū)蛹?jí)參數(shù)不對(duì)。如果瓦片目錄有但layer.json缺失檢查-c是否被漏掉。第一次跑可以用--help看當(dāng)前構(gòu)建支持的參數(shù)不同編譯版本會(huì)有細(xì)微差異不要硬套別人的命令。3.3 參數(shù)表瓦片尺寸、層級(jí)范圍、頂點(diǎn)壓縮、法線和水面掩膜參數(shù)常用值作用注意點(diǎn)-s/-e0 / 12-14起止層級(jí)結(jié)束層級(jí)不要高于數(shù)據(jù)分辨率30 米 DEM 切到 14 級(jí)以上收益很低-rbilinear重采樣方法陡峭地形可試 cubic不要用 nearest-N開啟頂點(diǎn)法線光照自然體積增大不開時(shí) Cesium 端不要請(qǐng)求法線-m有水系時(shí)開啟水面掩膜需要水體系數(shù)據(jù)參與不是所有 DEM 都自帶-c開啟生成 layer.json缺少時(shí) Cesium 無法識(shí)別瓦片規(guī)則-tCPU 核數(shù)一半線程數(shù)過高會(huì)搶內(nèi)存低配機(jī)器先從 4 開始-l自定義圖層名輸出圖層名不要用中文和空格避免路徑問題層級(jí)上限怎么定一個(gè)粗略算法是看源數(shù)據(jù)地面分辨率。比如 30 米 DEM在緯度 34 度附近Cesium 第 12 級(jí)的像素分辨率大約幾十米切到 12 或 13 級(jí)已經(jīng)能表達(dá)主要地形切到 15 級(jí)以上瓦片數(shù)量按四倍增長內(nèi)容卻多是插值出來的。我一般會(huì)先切 0 到 12 級(jí)做視覺驗(yàn)收確認(rèn)山脊、河谷、邊坡沒有明顯臺(tái)階再?zèng)Q定要不要加到 13 或 14。不要一上來切 18 級(jí)磁盤和瀏覽器內(nèi)存都會(huì)教你做人。水面掩膜要單獨(dú)說。-m不是“自動(dòng)識(shí)別水面”它需要額外的水體系數(shù)據(jù)或掩膜輸入。沒有水系數(shù)據(jù)時(shí)強(qiáng)行開啟可能生成空掩膜或者直接報(bào)錯(cuò)。Cesium 端requestWaterMask也要和切片參數(shù)對(duì)應(yīng)切片沒開水面掩膜前端就不要請(qǐng)求切片開了前端再按需開啟。法線同理-N和requestVertexNormals必須成對(duì)出現(xiàn)否則地形要么發(fā)黑要么光照平淡。3.4 切片產(chǎn)物怎么驗(yàn)收文件樹、瓦片頭與數(shù)量對(duì)賬切片完成后我習(xí)慣做三層驗(yàn)收目錄結(jié)構(gòu)、文件數(shù)量、瓦片頭。目錄結(jié)構(gòu)看z/x/y.terrain是否符合預(yù)期文件數(shù)量看是否和層級(jí)范圍大致匹配瓦片頭看文件是不是 gzip 或二進(jìn)制地形數(shù)據(jù)而不是空文件。下面幾條命令可以直接抄。# 統(tǒng)計(jì)地形瓦片數(shù)量 find ./terrain_tiles -type f -name *.terrain | wc -l # 查看前幾層目錄結(jié)構(gòu) find ./terrain_tiles -maxdepth 2 -type d | sort | head -20 # 抽查一個(gè)瓦片文件頭1f 8b 表示 gzip xxd -l 16 ./terrain_tiles/12/3376/1560.terrain # 查看 layer.json 的關(guān)鍵字段 python3 -c import json; mjson.load(open(./terrain_tiles/layer.json)); print(m.get(format), m.get(version), m.get(minzoom), m.get(maxzoom))find ... | wc -l是最快的數(shù)量對(duì)賬數(shù)量明顯偏少時(shí)先查層級(jí)范圍是不是寫錯(cuò)。find -maxdepth 2能看出層級(jí)目錄是否連續(xù)如果只有0/沒有1/說明切片在中途失敗。xxd看文件頭1f 8b是 gzip 魔數(shù)Cesium 地形瓦片經(jīng)常是 gzip 壓縮的服務(wù)器返回時(shí)要帶正確的Content-Encoding。layer.json里重點(diǎn)看format是否為quantized-mesh-1.0一類的地形格式minzoom和maxzoom是否和命令參數(shù)一致。字段名不同版本可能有差異但核心邏輯不變前端靠這份文件找瓦片字段錯(cuò)了就 404。4. 本地服務(wù)與 Cesium 加載把 layer.json 接進(jìn) Viewer 的最小閉環(huán)4.1 目錄結(jié)構(gòu)與 HTTP 服務(wù)不要直接雙擊 index.html離線地形最常見的第一個(gè)坑是直接雙擊 HTML 文件。file://協(xié)議下瀏覽器會(huì)攔截跨目錄請(qǐng)求layer.json和.terrain瓦片讀不出來控制臺(tái)一片紅。正確做法是起一個(gè)本地 HTTP 服務(wù)讓頁面和瓦片同源或者至少讓瓦片服務(wù)帶 CORS 頭。目錄可以這樣組織Cesium 靜態(tài)資源放Cesium/地形瓦片放terrain_tiles/頁面放根目錄。這樣layer.json里的相對(duì)路徑和頁面請(qǐng)求路徑容易對(duì)齊。# 在項(xiàng)目根目錄啟動(dòng)本地 HTTP 服務(wù)端口 8080 python3 -m http.server 8080 --bind 127.0.0.1--bind 127.0.0.1只允許本機(jī)訪問適合自己調(diào)試要給局域網(wǎng)其他機(jī)器看可以改成0.0.0.0但要注意內(nèi)網(wǎng)安全。Python 自帶的 HTTP 服務(wù)不會(huì)自動(dòng)加 CORS 頭如果頁面和瓦片不同源需要換成能配置 CORS 的服務(wù)器或者在反向代理里加Access-Control-Allow-Origin。另外.terrain文件的 MIME 類型不是所有服務(wù)器都認(rèn)識(shí)返回application/octet-stream通常能用關(guān)鍵是不要返回text/html。如果瓦片是 gzip 壓縮的服務(wù)器還要帶Content-Encoding: gzip否則瀏覽器會(huì)把壓縮數(shù)據(jù)當(dāng)壞文件。4.2 CesiumTerrainProvider 的關(guān)鍵參數(shù)requestVertexNormals 與 requestWaterMaskCesium 加載本地地形靠CesiumTerrainProviderurl指向包含layer.json的目錄不是指向layer.json文件本身。這一點(diǎn)很容易寫錯(cuò)寫成.../layer.json后請(qǐng)求路徑會(huì)多一層直接 404。requestVertexNormals對(duì)應(yīng)切片時(shí)的-N切片開了法線前端就設(shè)true地形光照會(huì)有立體感切片沒開前端設(shè)true也不會(huì)憑空生出法線反而可能報(bào)錯(cuò)或顯示異常。requestWaterMask對(duì)應(yīng)-m沒有水面掩膜就設(shè)false不要為了“看起來功能全”硬開。新版本 Cesium 常用CesiumTerrainProvider.fromUrl返回 Promise老版本用構(gòu)造函數(shù)直接 new。寫代碼時(shí)看控制臺(tái)報(bào)錯(cuò)判斷不要死記一種寫法。fromUrl的好處是能等layer.json加載完再設(shè)置viewer.terrainProvider避免地形還沒就緒就飛相機(jī)。如果項(xiàng)目里還用了在線影像或在線地形先把它們關(guān)掉確保請(qǐng)求只打到本地不然排查時(shí)分不清是本地瓦片問題還是網(wǎng)絡(luò)問題。4.3 一個(gè)最小可運(yùn)行的加載頁面下面這個(gè)頁面只做一件事加載本地地形把相機(jī)放到一個(gè)指定經(jīng)緯度。它沒有底圖沒有復(fù)雜控件方便先驗(yàn)證地形本身。實(shí)際項(xiàng)目再疊加本地影像或矢量。!DOCTYPE html html langzh-CN head meta charsetutf-8 title離線地形加載/title link relstylesheet href./Cesium/Widgets/widgets.css script src./Cesium/Cesium.js/script style html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } /style /head body div idcesiumContainer/div script (async () { const viewer new Cesium.Viewer(cesiumContainer, { imageryProvider: false, // 先關(guān)掉底圖只驗(yàn)證地形 baseLayerPicker: false, geocoder: false, homeButton: false, sceneModePicker: false, navigationHelpButton: false, animation: false, timeline: false }); // url 指向包含 layer.json 的目錄不是 layer.json 文件 const terrainProvider await Cesium.CesiumTerrainProvider.fromUrl( http://127.0.0.1:8080/terrain_tiles, { requestVertexNormals: true, // 與切片 -N 對(duì)應(yīng) requestWaterMask: false // 與切片 -m 對(duì)應(yīng) } ); viewer.terrainProvider terrainProvider; // 相機(jī)放到地形范圍附近高度先給大一點(diǎn)避免鉆進(jìn)地下 viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees(108.9, 34.2, 20000) }); })(); /script /body /htmlimageryProvider: false是為了排除底圖干擾地形加載失敗時(shí)不會(huì)誤以為是影像問題。fromUrl的第二個(gè)參數(shù)里requestVertexNormals和requestWaterMask必須和切片產(chǎn)物匹配。相機(jī)高度先給 20000 米等確認(rèn)地形范圍后再調(diào)低如果一開始就給 500 米很可能相機(jī)在地下或山體里看到的是一片黑。頁面和瓦片同源時(shí)url可以寫成相對(duì)路徑./terrain_tiles換機(jī)器部署時(shí)少改一處配置。4.4 離線場景下的跨域、緩存與路徑大小寫內(nèi)網(wǎng)部署時(shí)跨域和路徑大小寫是兩把鈍刀子??缬騿栴}常見于頁面在一個(gè)端口、瓦片在另一個(gè)端口瀏覽器控制臺(tái)會(huì)明確提示 CORS。解決方式不是在前端加mode: no-cors那只會(huì)讓響應(yīng)變成 opaqueCesium 讀不到內(nèi)容正確做法是讓瓦片服務(wù)返回Access-Control-Allow-Origin或者把頁面和瓦片放到同一個(gè)服務(wù)下。路徑大小寫問題多出現(xiàn)在 Linux 服務(wù)器Windows 上Terrain_Tiles和terrain_tiles不區(qū)分傳到 Linux 后 404layer.json里的模板和實(shí)際目錄必須完全一致。緩存也要注意。調(diào)試階段瀏覽器會(huì)緩存layer.json和失敗的瓦片請(qǐng)求改了切片參數(shù)后頁面沒變化先開無痕窗口或禁用緩存。生產(chǎn)環(huán)境則相反layer.json可以短緩存.terrain瓦片內(nèi)容不變時(shí)可以長緩存減少內(nèi)網(wǎng)請(qǐng)求。還有一個(gè)小細(xì)節(jié)layer.json里的tiles模板如果是{z}/{x}/{y}.terrain實(shí)際目錄就必須按這個(gè)結(jié)構(gòu)組織不要自作主張改成{z}/{y}/{x}.terrain。Cesium 不會(huì)猜你的目錄習(xí)慣模板和文件樹必須一一對(duì)應(yīng)。5. 避坑與排查離線地形加載失敗的 5 個(gè)現(xiàn)場5.1 地形整體偏移幾百米現(xiàn)象Cesium 里地形和模型、影像對(duì)不上整體平移幾百米山脊位置明顯偏。原因源 DEM 不是 WGS84或者gdalwarp的目標(biāo)坐標(biāo)系寫錯(cuò)也可能是經(jīng)緯度軸順序被調(diào)換。解決回到gdalinfo看源 CRS確認(rèn)-t_srs EPSG:4326生效用gdallocationinfo在已知地物點(diǎn)采樣高程和影像對(duì)比如果是軸順序問題檢查是否誤用了-s_srs或數(shù)據(jù)本身帶了非標(biāo)準(zhǔn)元數(shù)據(jù)。偏移問題不要靠前端平移模型去補(bǔ)補(bǔ)得越多后面越亂。5.2 瓦片 404layer.json 能讀到但地形不顯示現(xiàn)象控制臺(tái)請(qǐng)求.terrain返回 404layer.json本身 200。原因url指到了layer.json文件而不是目錄layer.json里的tiles模板和實(shí)際目錄不一致Linux 下路徑大小寫不匹配服務(wù)器根路徑多了或少了目錄。解決在瀏覽器里直接拼一個(gè)瓦片 URL 訪問看能不能下載對(duì)照layer.json的模板和find出來的文件樹把url改成目錄并以/結(jié)尾試一次檢查反向代理是否把/terrain_tiles重寫成了別的路徑。5.3 地形發(fā)黑、棱角明顯光照像被壓扁現(xiàn)象地形有起伏但表面發(fā)黑山脊像紙片旋轉(zhuǎn)視角時(shí)明暗不自然。原因切片時(shí)沒開-N前端卻設(shè)了requestVertexNormals: true或者切片開了法線前端沒請(qǐng)求也有可能是法線數(shù)據(jù)被服務(wù)器 gzip 處理壞了。解決先確認(rèn)切片命令有沒有-N再確認(rèn)前端參數(shù)是否一致用xxd看瓦片頭是否正常臨時(shí)把requestVertexNormals設(shè)為false對(duì)比效果。如果關(guān)掉法線后地形正常問題就在法線鏈路不要繼續(xù)調(diào)光照參數(shù)。5.4 水面沒有效果開了 requestWaterMask 也沒用現(xiàn)象地形加載正常但水面區(qū)域沒有反射或水面掩膜效果。原因切片時(shí)沒加-m或者沒有提供水體系數(shù)據(jù)前端requestWaterMask設(shè)了true但瓦片里沒有掩膜。解決先確認(rèn)切片命令是否帶-m再看輸出瓦片是否包含水面掩膜數(shù)據(jù)沒有水系數(shù)據(jù)時(shí)不要硬開前端設(shè)為false如果項(xiàng)目必須顯示水面單獨(dú)準(zhǔn)備水體系矢量或柵格掩膜再重新切片。水面效果不是前端一個(gè)開關(guān)能變出來的數(shù)據(jù)鏈路要完整。5.5 切到高層級(jí)后瀏覽器卡死或加載極慢現(xiàn)象低層級(jí)正常飛到近處后頁面卡頓內(nèi)存飆升瓦片請(qǐng)求越來越多。原因結(jié)束層級(jí)設(shè)得太高瓦片數(shù)量按四倍增長DEM 分辨率不夠高層級(jí)全是插值瓦片NoData 區(qū)域也被切片產(chǎn)生大量無效請(qǐng)求。解決把-e降下來30 米 DEM 先切到 12 或 13 級(jí)用范圍裁剪只切項(xiàng)目區(qū)域不要全省全國一起切檢查 NoData 區(qū)域是否被跳過瀏覽器端限制maximumScreenSpaceError不要讓 Cesium 無節(jié)制請(qǐng)求最高層級(jí)。層級(jí)不是越高越好夠用就行。6. 進(jìn)階把地形切片塞進(jìn)自動(dòng)化流水線與驗(yàn)收腳本切片命令跑通一次不難難的是每次數(shù)據(jù)更新都能穩(wěn)定復(fù)現(xiàn)。我一般會(huì)把預(yù)處理、切片、驗(yàn)收寫成三段腳本中間產(chǎn)物帶日期目錄失敗時(shí)保留日志。下面這個(gè) Python 腳本只做驗(yàn)收讀layer.json統(tǒng)計(jì)瓦片數(shù)量檢查關(guān)鍵字段。它不替代人工看地形但能攔住“目錄空了”“層級(jí)錯(cuò)了”“格式不對(duì)”這類低級(jí)問題。import json import os import glob base ./terrain_tiles layer_path os.path.join(base, layer.json) with open(layer_path, r, encodingutf-8) as f: meta json.load(f) print(format:, meta.get(format)) print(version:, meta.get(version)) print(minzoom:, meta.get(minzoom)) print(maxzoom:, meta.get(maxzoom)) print(tiles_template:, meta.get(tiles)) tiles glob.glob(os.path.join(base, *, *, *.terrain)) print(tile_count:, len(tiles)) # 抽查最大層級(jí)目錄是否存在 maxzoom meta.get(maxzoom) if maxzoom is not None: max_dir os.path.join(base, str(maxzoom)) print(maxzoom_dir_exists:, os.path.isdir(max_dir))format應(yīng)該是 quantized-mesh 相關(guān)格式tiles模板要和文件樹對(duì)得上tile_count為 0 就直接失敗不用再開瀏覽器。maxzoom_dir_exists能快速判斷切片是否只完成了一半。腳本跑完后我還會(huì)抽一個(gè)瓦片用xxd看文件頭確認(rèn)不是空文件。自動(dòng)化流水線里驗(yàn)收腳本失敗就不要發(fā)布別把問題留給前端。從那以后我每次切地形都強(qiáng)制走一遍“預(yù)處理檢查、切片參數(shù)對(duì)賬、瓦片數(shù)量驗(yàn)收”不再靠打開頁面看運(yùn)氣。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取