[{"data":1,"prerenderedAt":5021},["ShallowReactive",2],{"\u002Fblog\u002Fcesium-geojson-heatmap-sdk-implementation":3,"\u002Fblog\u002Fcesium-geojson-heatmap-sdk-implementation-surround":5014},{"id":4,"title":5,"author":6,"body":10,"date":5005,"description":5006,"extension":5007,"image":5008,"meta":5009,"minRead":228,"navigation":130,"path":5010,"seo":5011,"stem":5012,"__hash__":5013},"blog\u002Fblog\u002Fcesium-geojson-heatmap-sdk-implementation.md","Cesium GeoJSON 热力图 SDK 实现说明",{"name":7,"avatar":8},"Kevin",{"src":9,"alt":7},"\u002Favatar.jpg",{"type":11,"value":12,"toc":4986},"minimark",[13,17,21,48,55,59,62,391,394,421,424,574,577,580,627,630,634,645,656,663,886,897,900,995,1116,1119,1123,1136,1377,1380,1396,1400,1407,1410,1599,1605,1609,1616,1624,1627,1752,1755,1932,1935,2116,2127,2131,2140,2143,2289,2292,2425,2428,2778,2781,2799,2803,2806,2814,2820,3000,3006,3074,3080,3163,3174,3177,3180,3250,3253,3532,3535,3538,3541,3781,3784,3980,3983,3991,3995,4001,4021,4027,4031,4037,4192,4195,4212,4215,4218,4221,4313,4316,4327,4333,4510,4513,4517,4523,4594,4597,4614,4617,4624,4627,4633,4833,4836,4839,4958,4961,4964,4973,4976,4982],[14,15,16],"h2",{"id":16},"这篇笔记解决什么问题",[18,19,20],"p",{},"这个热力图实现的目标不是做一个只能服务某个 Demo 的页面效果，而是沉淀成一个通用 SDK 模块：",[22,23,24,33,36,39,45],"ul",{},[25,26,27,28,32],"li",{},"业务侧只负责提供 ",[29,30,31],"code",{},"FeatureCollection\u003CPoint>"," 格式的 GeoJSON 数据",[25,34,35],{},"SDK 内部负责从 GeoJSON 中解析经纬度和权重",[25,37,38],{},"SDK 内部负责把点数据绘制成二维 canvas 热力纹理",[25,40,41,42],{},"SDK 内部负责把热力纹理绑定到 Cesium 的 ",[29,43,44],{},"GroundPrimitive",[25,46,47],{},"半径、模糊度、透明度、显示目标、数据更新和销毁都通过统一 API 处理",[18,49,50,51,54],{},"所以这里最重要的设计边界是：Demo 页面只是使用者，真正可复用的能力在 ",[29,52,53],{},"demos\u002Fcesium\u002Fheatmap\u002F*"," 和 Cesium runtime 模块里。",[14,56,58],{"id":57},"对外-api-形态","对外 API 形态",[18,60,61],{},"业务接入时先用统一 runtime 加载 Cesium，再创建 Viewer，最后把 GeoJSON 传给热力图图层工厂。",[63,64,69],"pre",{"className":65,"code":66,"language":67,"meta":68,"style":68},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { loadCesium } from '.\u002FcesiumRuntime.js'\nimport { createGeoJsonHeatmapLayer } from '.\u002Fheatmap\u002FcesiumHeatmapLayer.js'\n\nconst Cesium = await loadCesium()\nconst viewer = new Cesium.Viewer(container)\n\nconst heatmapLayer = createGeoJsonHeatmapLayer(viewer, {\n  geoJson,\n  bounds: {\n    west: 113.7,\n    south: 22.4,\n    east: 114.7,\n    north: 22.9\n  },\n  valueProperty: 'value',\n  radius: 42,\n  blur: 32,\n  minOpacity: 0.12,\n  maxOpacity: 0.86,\n  visible: true,\n  classificationTarget: 'both'\n})\n","js","",[29,70,71,104,125,132,154,179,184,205,214,226,240,253,266,277,283,301,314,327,340,353,367,382],{"__ignoreMap":68},[72,73,76,80,84,88,91,94,97,101],"span",{"class":74,"line":75},"line",1,[72,77,79],{"class":78},"s7zQu","import",[72,81,83],{"class":82},"sMK4o"," {",[72,85,87],{"class":86},"sTEyZ"," loadCesium",[72,89,90],{"class":82}," }",[72,92,93],{"class":78}," from",[72,95,96],{"class":82}," '",[72,98,100],{"class":99},"sfazB",".\u002FcesiumRuntime.js",[72,102,103],{"class":82},"'\n",[72,105,107,109,111,114,116,118,120,123],{"class":74,"line":106},2,[72,108,79],{"class":78},[72,110,83],{"class":82},[72,112,113],{"class":86}," createGeoJsonHeatmapLayer",[72,115,90],{"class":82},[72,117,93],{"class":78},[72,119,96],{"class":82},[72,121,122],{"class":99},".\u002Fheatmap\u002FcesiumHeatmapLayer.js",[72,124,103],{"class":82},[72,126,128],{"class":74,"line":127},3,[72,129,131],{"emptyLinePlaceholder":130},true,"\n",[72,133,135,139,142,145,148,151],{"class":74,"line":134},4,[72,136,138],{"class":137},"spNyl","const",[72,140,141],{"class":86}," Cesium ",[72,143,144],{"class":82},"=",[72,146,147],{"class":78}," await",[72,149,87],{"class":150},"s2Zo4",[72,152,153],{"class":86},"()\n",[72,155,157,159,162,164,167,170,173,176],{"class":74,"line":156},5,[72,158,138],{"class":137},[72,160,161],{"class":86}," viewer ",[72,163,144],{"class":82},[72,165,166],{"class":82}," new",[72,168,169],{"class":86}," Cesium",[72,171,172],{"class":82},".",[72,174,175],{"class":150},"Viewer",[72,177,178],{"class":86},"(container)\n",[72,180,182],{"class":74,"line":181},6,[72,183,131],{"emptyLinePlaceholder":130},[72,185,187,189,192,194,196,199,202],{"class":74,"line":186},7,[72,188,138],{"class":137},[72,190,191],{"class":86}," heatmapLayer ",[72,193,144],{"class":82},[72,195,113],{"class":150},[72,197,198],{"class":86},"(viewer",[72,200,201],{"class":82},",",[72,203,204],{"class":82}," {\n",[72,206,208,211],{"class":74,"line":207},8,[72,209,210],{"class":86},"  geoJson",[72,212,213],{"class":82},",\n",[72,215,217,221,224],{"class":74,"line":216},9,[72,218,220],{"class":219},"swJcz","  bounds",[72,222,223],{"class":82},":",[72,225,204],{"class":82},[72,227,229,232,234,238],{"class":74,"line":228},10,[72,230,231],{"class":219},"    west",[72,233,223],{"class":82},[72,235,237],{"class":236},"sbssI"," 113.7",[72,239,213],{"class":82},[72,241,243,246,248,251],{"class":74,"line":242},11,[72,244,245],{"class":219},"    south",[72,247,223],{"class":82},[72,249,250],{"class":236}," 22.4",[72,252,213],{"class":82},[72,254,256,259,261,264],{"class":74,"line":255},12,[72,257,258],{"class":219},"    east",[72,260,223],{"class":82},[72,262,263],{"class":236}," 114.7",[72,265,213],{"class":82},[72,267,269,272,274],{"class":74,"line":268},13,[72,270,271],{"class":219},"    north",[72,273,223],{"class":82},[72,275,276],{"class":236}," 22.9\n",[72,278,280],{"class":74,"line":279},14,[72,281,282],{"class":82},"  },\n",[72,284,286,289,291,293,296,299],{"class":74,"line":285},15,[72,287,288],{"class":219},"  valueProperty",[72,290,223],{"class":82},[72,292,96],{"class":82},[72,294,295],{"class":99},"value",[72,297,298],{"class":82},"'",[72,300,213],{"class":82},[72,302,304,307,309,312],{"class":74,"line":303},16,[72,305,306],{"class":219},"  radius",[72,308,223],{"class":82},[72,310,311],{"class":236}," 42",[72,313,213],{"class":82},[72,315,317,320,322,325],{"class":74,"line":316},17,[72,318,319],{"class":219},"  blur",[72,321,223],{"class":82},[72,323,324],{"class":236}," 32",[72,326,213],{"class":82},[72,328,330,333,335,338],{"class":74,"line":329},18,[72,331,332],{"class":219},"  minOpacity",[72,334,223],{"class":82},[72,336,337],{"class":236}," 0.12",[72,339,213],{"class":82},[72,341,343,346,348,351],{"class":74,"line":342},19,[72,344,345],{"class":219},"  maxOpacity",[72,347,223],{"class":82},[72,349,350],{"class":236}," 0.86",[72,352,213],{"class":82},[72,354,356,359,361,365],{"class":74,"line":355},20,[72,357,358],{"class":219},"  visible",[72,360,223],{"class":82},[72,362,364],{"class":363},"sfNiH"," true",[72,366,213],{"class":82},[72,368,370,373,375,377,380],{"class":74,"line":369},21,[72,371,372],{"class":219},"  classificationTarget",[72,374,223],{"class":82},[72,376,96],{"class":82},[72,378,379],{"class":99},"both",[72,381,103],{"class":82},[72,383,385,388],{"class":74,"line":384},22,[72,386,387],{"class":82},"}",[72,389,390],{"class":86},")\n",[18,392,393],{},"这个 API 有三个关键点：",[22,395,396,403,414],{},[25,397,398,399,402],{},"业务代码不需要把 ",[29,400,401],{},"Cesium"," 参数继续传进热力图模块。",[25,404,405,406,409,410,413],{},"覆盖范围通过 ",[29,407,408],{},"bounds"," 表达，SDK 只认 ",[29,411,412],{},"west\u002Fsouth\u002Feast\u002Fnorth","。",[25,415,416,417,420],{},"显示目标通过 ",[29,418,419],{},"'terrain' | 'tiles' | 'both'"," 控制，而不是额外绑一个 tileset 实例。",[18,422,423],{},"图层对象返回后，后续更新都走同一个对象：",[63,425,427],{"className":65,"code":426,"language":67,"meta":68,"style":68},"heatmapLayer.updateGeoJson(nextGeoJson)\n\nheatmapLayer.setOptions({\n  radius: 56,\n  blur: 40,\n  minOpacity: 0.08,\n  maxOpacity: 0.9,\n  classificationTarget: 'tiles'\n})\n\nheatmapLayer.setVisible(false)\nheatmapLayer.setClassificationTarget('both')\nheatmapLayer.destroy()\n",[29,428,429,442,446,461,472,483,494,505,518,524,528,544,563],{"__ignoreMap":68},[72,430,431,434,436,439],{"class":74,"line":75},[72,432,433],{"class":86},"heatmapLayer",[72,435,172],{"class":82},[72,437,438],{"class":150},"updateGeoJson",[72,440,441],{"class":86},"(nextGeoJson)\n",[72,443,444],{"class":74,"line":106},[72,445,131],{"emptyLinePlaceholder":130},[72,447,448,450,452,455,458],{"class":74,"line":127},[72,449,433],{"class":86},[72,451,172],{"class":82},[72,453,454],{"class":150},"setOptions",[72,456,457],{"class":86},"(",[72,459,460],{"class":82},"{\n",[72,462,463,465,467,470],{"class":74,"line":134},[72,464,306],{"class":219},[72,466,223],{"class":82},[72,468,469],{"class":236}," 56",[72,471,213],{"class":82},[72,473,474,476,478,481],{"class":74,"line":156},[72,475,319],{"class":219},[72,477,223],{"class":82},[72,479,480],{"class":236}," 40",[72,482,213],{"class":82},[72,484,485,487,489,492],{"class":74,"line":181},[72,486,332],{"class":219},[72,488,223],{"class":82},[72,490,491],{"class":236}," 0.08",[72,493,213],{"class":82},[72,495,496,498,500,503],{"class":74,"line":186},[72,497,345],{"class":219},[72,499,223],{"class":82},[72,501,502],{"class":236}," 0.9",[72,504,213],{"class":82},[72,506,507,509,511,513,516],{"class":74,"line":207},[72,508,372],{"class":219},[72,510,223],{"class":82},[72,512,96],{"class":82},[72,514,515],{"class":99},"tiles",[72,517,103],{"class":82},[72,519,520,522],{"class":74,"line":216},[72,521,387],{"class":82},[72,523,390],{"class":86},[72,525,526],{"class":74,"line":228},[72,527,131],{"emptyLinePlaceholder":130},[72,529,530,532,534,537,539,542],{"class":74,"line":242},[72,531,433],{"class":86},[72,533,172],{"class":82},[72,535,536],{"class":150},"setVisible",[72,538,457],{"class":86},[72,540,541],{"class":363},"false",[72,543,390],{"class":86},[72,545,546,548,550,553,555,557,559,561],{"class":74,"line":255},[72,547,433],{"class":86},[72,549,172],{"class":82},[72,551,552],{"class":150},"setClassificationTarget",[72,554,457],{"class":86},[72,556,298],{"class":82},[72,558,379],{"class":99},[72,560,298],{"class":82},[72,562,390],{"class":86},[72,564,565,567,569,572],{"class":74,"line":268},[72,566,433],{"class":86},[72,568,172],{"class":82},[72,570,571],{"class":150},"destroy",[72,573,153],{"class":86},[14,575,576],{"id":576},"整体链路",[18,578,579],{},"当前实现可以按下面这条链路理解：",[581,582,583,589,594,600,606,612,621,624],"ol",{},[25,584,585,588],{},[29,586,587],{},"loadCesium()"," 统一加载 Cesium，并缓存 Cesium module。",[25,590,591,592,413],{},"Demo 或业务代码创建 Cesium ",[29,593,175],{},[25,595,596,599],{},[29,597,598],{},"createGeoJsonHeatmapLayer(viewer, options)"," 创建热力图图层。",[25,601,602,605],{},[29,603,604],{},"parseGeoJsonPoints()"," 校验 GeoJSON，并提取经纬度、权重和统计信息。",[25,607,608,611],{},[29,609,610],{},"renderHeatmapCanvas()"," 把点位投影到 canvas，并生成热力纹理。",[25,613,614,616,617,620],{},[29,615,44],{}," 读取热力 canvas，并按 ",[29,618,619],{},"classificationTarget"," 分类到地形、3D Tiles 或两者。",[25,622,623],{},"参数或数据变化时，SDK 只重绘热力图，必要时重建 primitive。",[25,625,626],{},"页面销毁时，SDK 移除 primitive，并释放内部状态。",[18,628,629],{},"这条链路把“数据解析”“纹理绘制”“Cesium 绑定”“生命周期”拆开了。后续换真实接口数据、换业务范围、换权重字段时，不需要重写热力图渲染逻辑。",[14,631,633],{"id":632},"cesium-runtime-为什么要单例化","Cesium runtime 为什么要单例化",[18,635,636,637,640,641,644],{},"之前每个 Cesium Demo 都自己设置 ",[29,638,639],{},"CESIUM_BASE_URL","、注入 Widgets CSS、动态导入 ",[29,642,643],{},"\u002Fcesium\u002Findex.js","。这些逻辑散在多个文件里，会带来两个问题：",[22,646,647,650],{},[25,648,649],{},"每个 Demo 都有重复初始化代码。",[25,651,652,653,655],{},"热力图 SDK 如果也要求传入 ",[29,654,401],{},"，业务调用会变得啰嗦。",[18,657,658,659,662],{},"现在统一收敛到 ",[29,660,661],{},"demos\u002Fcesium\u002FcesiumRuntime.js","：",[63,664,666],{"className":65,"code":665,"language":67,"meta":68,"style":68},"let cesiumPromise = null\nlet cesiumModule = null\n\nexport async function loadCesium() {\n  ensureCesiumBaseUrl()\n  ensureCesiumWidgetsStyles()\n\n  if (!cesiumPromise) {\n    cesiumPromise = import(\u002F* @vite-ignore *\u002F CESIUM_RUNTIME_URL)\n      .then((module) => {\n        cesiumModule = module\n        return module\n      })\n      .catch((error) => {\n        cesiumPromise = null\n        throw error\n      })\n  }\n\n  return cesiumPromise\n}\n",[29,667,668,681,692,696,714,721,728,732,751,773,797,807,814,821,841,850,858,864,869,873,881],{"__ignoreMap":68},[72,669,670,673,676,678],{"class":74,"line":75},[72,671,672],{"class":137},"let",[72,674,675],{"class":86}," cesiumPromise ",[72,677,144],{"class":82},[72,679,680],{"class":82}," null\n",[72,682,683,685,688,690],{"class":74,"line":106},[72,684,672],{"class":137},[72,686,687],{"class":86}," cesiumModule ",[72,689,144],{"class":82},[72,691,680],{"class":82},[72,693,694],{"class":74,"line":127},[72,695,131],{"emptyLinePlaceholder":130},[72,697,698,701,704,707,709,712],{"class":74,"line":134},[72,699,700],{"class":78},"export",[72,702,703],{"class":137}," async",[72,705,706],{"class":137}," function",[72,708,87],{"class":150},[72,710,711],{"class":82},"()",[72,713,204],{"class":82},[72,715,716,719],{"class":74,"line":156},[72,717,718],{"class":150},"  ensureCesiumBaseUrl",[72,720,153],{"class":219},[72,722,723,726],{"class":74,"line":181},[72,724,725],{"class":150},"  ensureCesiumWidgetsStyles",[72,727,153],{"class":219},[72,729,730],{"class":74,"line":186},[72,731,131],{"emptyLinePlaceholder":130},[72,733,734,737,740,743,746,749],{"class":74,"line":207},[72,735,736],{"class":78},"  if",[72,738,739],{"class":219}," (",[72,741,742],{"class":82},"!",[72,744,745],{"class":86},"cesiumPromise",[72,747,748],{"class":219},") ",[72,750,460],{"class":82},[72,752,753,756,759,762,764,768,771],{"class":74,"line":216},[72,754,755],{"class":86},"    cesiumPromise",[72,757,758],{"class":82}," =",[72,760,761],{"class":150}," import",[72,763,457],{"class":219},[72,765,767],{"class":766},"sHwdD","\u002F* @vite-ignore *\u002F",[72,769,770],{"class":86}," CESIUM_RUNTIME_URL",[72,772,390],{"class":219},[72,774,775,778,781,783,785,789,792,795],{"class":74,"line":228},[72,776,777],{"class":82},"      .",[72,779,780],{"class":150},"then",[72,782,457],{"class":219},[72,784,457],{"class":82},[72,786,788],{"class":787},"sHdIc","module",[72,790,791],{"class":82},")",[72,793,794],{"class":137}," =>",[72,796,204],{"class":82},[72,798,799,802,804],{"class":74,"line":242},[72,800,801],{"class":86},"        cesiumModule",[72,803,758],{"class":82},[72,805,806],{"class":82}," module\n",[72,808,809,812],{"class":74,"line":255},[72,810,811],{"class":78},"        return",[72,813,806],{"class":82},[72,815,816,819],{"class":74,"line":268},[72,817,818],{"class":82},"      }",[72,820,390],{"class":219},[72,822,823,825,828,830,832,835,837,839],{"class":74,"line":279},[72,824,777],{"class":82},[72,826,827],{"class":150},"catch",[72,829,457],{"class":219},[72,831,457],{"class":82},[72,833,834],{"class":787},"error",[72,836,791],{"class":82},[72,838,794],{"class":137},[72,840,204],{"class":82},[72,842,843,846,848],{"class":74,"line":285},[72,844,845],{"class":86},"        cesiumPromise",[72,847,758],{"class":82},[72,849,680],{"class":82},[72,851,852,855],{"class":74,"line":303},[72,853,854],{"class":78},"        throw",[72,856,857],{"class":86}," error\n",[72,859,860,862],{"class":74,"line":316},[72,861,818],{"class":82},[72,863,390],{"class":219},[72,865,866],{"class":74,"line":329},[72,867,868],{"class":82},"  }\n",[72,870,871],{"class":74,"line":342},[72,872,131],{"emptyLinePlaceholder":130},[72,874,875,878],{"class":74,"line":355},[72,876,877],{"class":78},"  return",[72,879,880],{"class":86}," cesiumPromise\n",[72,882,883],{"class":74,"line":369},[72,884,885],{"class":82},"}\n",[18,887,888,889,892,893,896],{},"热力图模块内部通过 ",[29,890,891],{},"getCesium()"," 读取已经加载好的 Cesium。这样业务入口只需要保证先 ",[29,894,895],{},"await loadCesium()","，后面的通用模块就能直接工作。",[18,898,899],{},"为了让 Demo 能直接加载 Cesium World Terrain 和 OSM Buildings，runtime 现在还会在模块初始化时同步注入 Ion token：",[63,901,903],{"className":65,"code":902,"language":67,"meta":68,"style":68},"function resolveCesiumIonToken() {\n  if (typeof window === 'undefined') {\n    return ''\n  }\n\n  return window.__NUXT__?.config?.public?.cesiumIonToken || ''\n}\n",[29,904,905,917,943,951,955,959,991],{"__ignoreMap":68},[72,906,907,910,913,915],{"class":74,"line":75},[72,908,909],{"class":137},"function",[72,911,912],{"class":150}," resolveCesiumIonToken",[72,914,711],{"class":82},[72,916,204],{"class":82},[72,918,919,921,923,926,929,932,934,937,939,941],{"class":74,"line":106},[72,920,736],{"class":78},[72,922,739],{"class":219},[72,924,925],{"class":82},"typeof",[72,927,928],{"class":86}," window",[72,930,931],{"class":82}," ===",[72,933,96],{"class":82},[72,935,936],{"class":99},"undefined",[72,938,298],{"class":82},[72,940,748],{"class":219},[72,942,460],{"class":82},[72,944,945,948],{"class":74,"line":127},[72,946,947],{"class":78},"    return",[72,949,950],{"class":82}," ''\n",[72,952,953],{"class":74,"line":134},[72,954,868],{"class":82},[72,956,957],{"class":74,"line":156},[72,958,131],{"emptyLinePlaceholder":130},[72,960,961,963,965,967,970,973,976,978,981,983,986,989],{"class":74,"line":181},[72,962,877],{"class":78},[72,964,928],{"class":86},[72,966,172],{"class":82},[72,968,969],{"class":86},"__NUXT__",[72,971,972],{"class":82},"?.",[72,974,975],{"class":86},"config",[72,977,972],{"class":82},[72,979,980],{"class":86},"public",[72,982,972],{"class":82},[72,984,985],{"class":86},"cesiumIonToken",[72,987,988],{"class":82}," ||",[72,990,950],{"class":82},[72,992,993],{"class":74,"line":186},[72,994,885],{"class":82},[63,996,998],{"className":65,"code":997,"language":67,"meta":68,"style":68},"cesiumPromise = import(\u002F* @vite-ignore *\u002F CESIUM_RUNTIME_URL)\n  .then((module) => {\n    const ionToken = resolveCesiumIonToken()\n\n    if (ionToken) {\n      module.Ion.defaultAccessToken = ionToken\n    }\n\n    cesiumModule = module\n    return module\n  })\n",[29,999,1000,1016,1035,1049,1053,1067,1085,1090,1094,1103,1109],{"__ignoreMap":68},[72,1001,1002,1005,1007,1009,1011,1013],{"class":74,"line":75},[72,1003,1004],{"class":86},"cesiumPromise ",[72,1006,144],{"class":82},[72,1008,761],{"class":150},[72,1010,457],{"class":86},[72,1012,767],{"class":766},[72,1014,1015],{"class":86}," CESIUM_RUNTIME_URL)\n",[72,1017,1018,1021,1023,1025,1027,1029,1031,1033],{"class":74,"line":106},[72,1019,1020],{"class":82},"  .",[72,1022,780],{"class":150},[72,1024,457],{"class":86},[72,1026,457],{"class":82},[72,1028,788],{"class":787},[72,1030,791],{"class":82},[72,1032,794],{"class":137},[72,1034,204],{"class":82},[72,1036,1037,1040,1043,1045,1047],{"class":74,"line":127},[72,1038,1039],{"class":137},"    const",[72,1041,1042],{"class":86}," ionToken",[72,1044,758],{"class":82},[72,1046,912],{"class":150},[72,1048,153],{"class":219},[72,1050,1051],{"class":74,"line":134},[72,1052,131],{"emptyLinePlaceholder":130},[72,1054,1055,1058,1060,1063,1065],{"class":74,"line":156},[72,1056,1057],{"class":78},"    if",[72,1059,739],{"class":219},[72,1061,1062],{"class":86},"ionToken",[72,1064,748],{"class":219},[72,1066,460],{"class":82},[72,1068,1069,1072,1075,1077,1080,1082],{"class":74,"line":181},[72,1070,1071],{"class":82},"      module.",[72,1073,1074],{"class":86},"Ion",[72,1076,172],{"class":82},[72,1078,1079],{"class":86},"defaultAccessToken",[72,1081,758],{"class":82},[72,1083,1084],{"class":86}," ionToken\n",[72,1086,1087],{"class":74,"line":186},[72,1088,1089],{"class":82},"    }\n",[72,1091,1092],{"class":74,"line":207},[72,1093,131],{"emptyLinePlaceholder":130},[72,1095,1096,1099,1101],{"class":74,"line":216},[72,1097,1098],{"class":86},"    cesiumModule",[72,1100,758],{"class":82},[72,1102,806],{"class":82},[72,1104,1105,1107],{"class":74,"line":228},[72,1106,947],{"class":78},[72,1108,806],{"class":82},[72,1110,1111,1114],{"class":74,"line":242},[72,1112,1113],{"class":82},"  }",[72,1115,390],{"class":86},[18,1117,1118],{},"这样 Demo 页面里的 terrain \u002F 3D Tiles 加载，就不需要再到业务层单独处理 token 传递。",[14,1120,1122],{"id":1121},"geojson-数据约定","GeoJSON 数据约定",[18,1124,1125,1126,1128,1129,1132,1133,413],{},"热力图输入固定使用 ",[29,1127,31],{},"。坐标使用 WGS84，经纬度顺序为 ",[29,1130,1131],{},"[longitude, latitude]","。权重字段默认读取 ",[29,1134,1135],{},"properties.value",[63,1137,1141],{"className":1138,"code":1139,"language":1140,"meta":68,"style":68},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"id\": \"heat-001\",\n        \"name\": \"sample point\",\n        \"category\": \"event\",\n        \"value\": 92\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [113.941, 22.541]\n      }\n    }\n  ]\n}\n","json",[29,1142,1143,1147,1170,1184,1189,1210,1223,1244,1264,1284,1297,1302,1315,1334,1359,1364,1368,1373],{"__ignoreMap":68},[72,1144,1145],{"class":74,"line":75},[72,1146,460],{"class":82},[72,1148,1149,1152,1155,1158,1160,1163,1166,1168],{"class":74,"line":106},[72,1150,1151],{"class":82},"  \"",[72,1153,1154],{"class":137},"type",[72,1156,1157],{"class":82},"\"",[72,1159,223],{"class":82},[72,1161,1162],{"class":82}," \"",[72,1164,1165],{"class":99},"FeatureCollection",[72,1167,1157],{"class":82},[72,1169,213],{"class":82},[72,1171,1172,1174,1177,1179,1181],{"class":74,"line":127},[72,1173,1151],{"class":82},[72,1175,1176],{"class":137},"features",[72,1178,1157],{"class":82},[72,1180,223],{"class":82},[72,1182,1183],{"class":82}," [\n",[72,1185,1186],{"class":74,"line":134},[72,1187,1188],{"class":82},"    {\n",[72,1190,1191,1194,1197,1199,1201,1203,1206,1208],{"class":74,"line":156},[72,1192,1193],{"class":82},"      \"",[72,1195,1154],{"class":1196},"sBMFI",[72,1198,1157],{"class":82},[72,1200,223],{"class":82},[72,1202,1162],{"class":82},[72,1204,1205],{"class":99},"Feature",[72,1207,1157],{"class":82},[72,1209,213],{"class":82},[72,1211,1212,1214,1217,1219,1221],{"class":74,"line":181},[72,1213,1193],{"class":82},[72,1215,1216],{"class":1196},"properties",[72,1218,1157],{"class":82},[72,1220,223],{"class":82},[72,1222,204],{"class":82},[72,1224,1225,1228,1231,1233,1235,1237,1240,1242],{"class":74,"line":186},[72,1226,1227],{"class":82},"        \"",[72,1229,1230],{"class":236},"id",[72,1232,1157],{"class":82},[72,1234,223],{"class":82},[72,1236,1162],{"class":82},[72,1238,1239],{"class":99},"heat-001",[72,1241,1157],{"class":82},[72,1243,213],{"class":82},[72,1245,1246,1248,1251,1253,1255,1257,1260,1262],{"class":74,"line":207},[72,1247,1227],{"class":82},[72,1249,1250],{"class":236},"name",[72,1252,1157],{"class":82},[72,1254,223],{"class":82},[72,1256,1162],{"class":82},[72,1258,1259],{"class":99},"sample point",[72,1261,1157],{"class":82},[72,1263,213],{"class":82},[72,1265,1266,1268,1271,1273,1275,1277,1280,1282],{"class":74,"line":216},[72,1267,1227],{"class":82},[72,1269,1270],{"class":236},"category",[72,1272,1157],{"class":82},[72,1274,223],{"class":82},[72,1276,1162],{"class":82},[72,1278,1279],{"class":99},"event",[72,1281,1157],{"class":82},[72,1283,213],{"class":82},[72,1285,1286,1288,1290,1292,1294],{"class":74,"line":228},[72,1287,1227],{"class":82},[72,1289,295],{"class":236},[72,1291,1157],{"class":82},[72,1293,223],{"class":82},[72,1295,1296],{"class":236}," 92\n",[72,1298,1299],{"class":74,"line":242},[72,1300,1301],{"class":82},"      },\n",[72,1303,1304,1306,1309,1311,1313],{"class":74,"line":255},[72,1305,1193],{"class":82},[72,1307,1308],{"class":1196},"geometry",[72,1310,1157],{"class":82},[72,1312,223],{"class":82},[72,1314,204],{"class":82},[72,1316,1317,1319,1321,1323,1325,1327,1330,1332],{"class":74,"line":268},[72,1318,1227],{"class":82},[72,1320,1154],{"class":236},[72,1322,1157],{"class":82},[72,1324,223],{"class":82},[72,1326,1162],{"class":82},[72,1328,1329],{"class":99},"Point",[72,1331,1157],{"class":82},[72,1333,213],{"class":82},[72,1335,1336,1338,1341,1343,1345,1348,1351,1353,1356],{"class":74,"line":279},[72,1337,1227],{"class":82},[72,1339,1340],{"class":236},"coordinates",[72,1342,1157],{"class":82},[72,1344,223],{"class":82},[72,1346,1347],{"class":82}," [",[72,1349,1350],{"class":236},"113.941",[72,1352,201],{"class":82},[72,1354,1355],{"class":236}," 22.541",[72,1357,1358],{"class":82},"]\n",[72,1360,1361],{"class":74,"line":285},[72,1362,1363],{"class":82},"      }\n",[72,1365,1366],{"class":74,"line":303},[72,1367,1089],{"class":82},[72,1369,1370],{"class":74,"line":316},[72,1371,1372],{"class":82},"  ]\n",[72,1374,1375],{"class":74,"line":329},[72,1376,885],{"class":82},[18,1378,1379],{},"SDK 只依赖两部分数据：",[22,1381,1382,1388],{},[25,1383,1384,1387],{},[29,1385,1386],{},"geometry.coordinates","：点位经纬度",[25,1389,1390,1393,1394],{},[29,1391,1392],{},"properties[valueProperty]","：点位权重，默认是 ",[29,1395,1135],{},[14,1397,1399],{"id":1398},"geojson-怎么被解析","GeoJSON 怎么被解析",[18,1401,1402,1403,1406],{},"解析逻辑在 ",[29,1404,1405],{},"demos\u002Fcesium\u002Fheatmap\u002Fgeojson.js","。它不会因为某一个坏点中断整张热力图，而是跳过无效点，并返回统计信息。",[18,1408,1409],{},"关键判断如下：",[63,1411,1413],{"className":65,"code":1412,"language":67,"meta":68,"style":68},"const isPointFeature = feature?.type === 'Feature' &&\n  geometry?.type === 'Point' &&\n  Array.isArray(coordinates) &&\n  coordinates.length >= 2\n\nif (!isPointFeature || !Number.isFinite(coordinates[0]) || !Number.isFinite(coordinates[1]) || !Number.isFinite(value) || value \u003C 0) {\n  skipped += 1\n  continue\n}\n",[29,1414,1415,1444,1463,1479,1495,1499,1579,1590,1595],{"__ignoreMap":68},[72,1416,1417,1419,1422,1424,1427,1429,1432,1435,1437,1439,1441],{"class":74,"line":75},[72,1418,138],{"class":137},[72,1420,1421],{"class":86}," isPointFeature ",[72,1423,144],{"class":82},[72,1425,1426],{"class":86}," feature",[72,1428,972],{"class":82},[72,1430,1431],{"class":86},"type ",[72,1433,1434],{"class":82},"===",[72,1436,96],{"class":82},[72,1438,1205],{"class":99},[72,1440,298],{"class":82},[72,1442,1443],{"class":82}," &&\n",[72,1445,1446,1449,1451,1453,1455,1457,1459,1461],{"class":74,"line":106},[72,1447,1448],{"class":86},"  geometry",[72,1450,972],{"class":82},[72,1452,1431],{"class":86},[72,1454,1434],{"class":82},[72,1456,96],{"class":82},[72,1458,1329],{"class":99},[72,1460,298],{"class":82},[72,1462,1443],{"class":82},[72,1464,1465,1468,1470,1473,1476],{"class":74,"line":127},[72,1466,1467],{"class":86},"  Array",[72,1469,172],{"class":82},[72,1471,1472],{"class":150},"isArray",[72,1474,1475],{"class":86},"(coordinates) ",[72,1477,1478],{"class":82},"&&\n",[72,1480,1481,1484,1486,1489,1492],{"class":74,"line":134},[72,1482,1483],{"class":86},"  coordinates",[72,1485,172],{"class":82},[72,1487,1488],{"class":86},"length ",[72,1490,1491],{"class":82},">=",[72,1493,1494],{"class":236}," 2\n",[72,1496,1497],{"class":74,"line":156},[72,1498,131],{"emptyLinePlaceholder":130},[72,1500,1501,1504,1506,1508,1511,1514,1517,1520,1522,1525,1528,1531,1534,1536,1538,1540,1542,1544,1546,1549,1551,1553,1555,1557,1559,1561,1564,1566,1569,1572,1575,1577],{"class":74,"line":181},[72,1502,1503],{"class":78},"if",[72,1505,739],{"class":86},[72,1507,742],{"class":82},[72,1509,1510],{"class":86},"isPointFeature ",[72,1512,1513],{"class":82},"||",[72,1515,1516],{"class":82}," !",[72,1518,1519],{"class":86},"Number",[72,1521,172],{"class":82},[72,1523,1524],{"class":150},"isFinite",[72,1526,1527],{"class":86},"(coordinates[",[72,1529,1530],{"class":236},"0",[72,1532,1533],{"class":86},"]) ",[72,1535,1513],{"class":82},[72,1537,1516],{"class":82},[72,1539,1519],{"class":86},[72,1541,172],{"class":82},[72,1543,1524],{"class":150},[72,1545,1527],{"class":86},[72,1547,1548],{"class":236},"1",[72,1550,1533],{"class":86},[72,1552,1513],{"class":82},[72,1554,1516],{"class":82},[72,1556,1519],{"class":86},[72,1558,172],{"class":82},[72,1560,1524],{"class":150},[72,1562,1563],{"class":86},"(value) ",[72,1565,1513],{"class":82},[72,1567,1568],{"class":86}," value ",[72,1570,1571],{"class":82},"\u003C",[72,1573,1574],{"class":236}," 0",[72,1576,748],{"class":86},[72,1578,460],{"class":82},[72,1580,1581,1584,1587],{"class":74,"line":186},[72,1582,1583],{"class":86},"  skipped",[72,1585,1586],{"class":82}," +=",[72,1588,1589],{"class":236}," 1\n",[72,1591,1592],{"class":74,"line":207},[72,1593,1594],{"class":78},"  continue\n",[72,1596,1597],{"class":74,"line":216},[72,1598,885],{"class":82},[18,1600,1601,1602,1604],{},"然后再用 ",[29,1603,408],{}," 判断点是否落在当前热力图覆盖范围内。这样业务接入时只需要明确覆盖范围，解析器就能稳定产出当前图层应该消费的点集。",[14,1606,1608],{"id":1607},"canvas-热力纹理怎么生成","Canvas 热力纹理怎么生成",[18,1610,1611,1612,1615],{},"真正生成热力图的是 ",[29,1613,1614],{},"demos\u002Fcesium\u002Fheatmap\u002FcanvasHeatmap.js","。它把热力图拆成两步：",[581,1617,1618,1621],{},[25,1619,1620],{},"先绘制灰度密度图",[25,1622,1623],{},"再把灰度 alpha 映射成热力颜色和透明度",[18,1625,1626],{},"生成单个点的影响范围时，使用径向渐变：",[63,1628,1630],{"className":65,"code":1629,"language":67,"meta":68,"style":68},"const innerRadius = clamp(radius - blur, 0, radius)\nconst gradient = context.createRadialGradient(center, center, innerRadius, center, center, radius)\ngradient.addColorStop(0, 'rgba(0, 0, 0, 1)')\ngradient.addColorStop(1, 'rgba(0, 0, 0, 0)')\n",[29,1631,1632,1662,1704,1729],{"__ignoreMap":68},[72,1633,1634,1636,1639,1641,1644,1647,1650,1653,1655,1657,1659],{"class":74,"line":75},[72,1635,138],{"class":137},[72,1637,1638],{"class":86}," innerRadius ",[72,1640,144],{"class":82},[72,1642,1643],{"class":150}," clamp",[72,1645,1646],{"class":86},"(radius ",[72,1648,1649],{"class":82},"-",[72,1651,1652],{"class":86}," blur",[72,1654,201],{"class":82},[72,1656,1574],{"class":236},[72,1658,201],{"class":82},[72,1660,1661],{"class":86}," radius)\n",[72,1663,1664,1666,1669,1671,1674,1676,1679,1682,1684,1687,1689,1692,1694,1696,1698,1700,1702],{"class":74,"line":106},[72,1665,138],{"class":137},[72,1667,1668],{"class":86}," gradient ",[72,1670,144],{"class":82},[72,1672,1673],{"class":86}," context",[72,1675,172],{"class":82},[72,1677,1678],{"class":150},"createRadialGradient",[72,1680,1681],{"class":86},"(center",[72,1683,201],{"class":82},[72,1685,1686],{"class":86}," center",[72,1688,201],{"class":82},[72,1690,1691],{"class":86}," innerRadius",[72,1693,201],{"class":82},[72,1695,1686],{"class":86},[72,1697,201],{"class":82},[72,1699,1686],{"class":86},[72,1701,201],{"class":82},[72,1703,1661],{"class":86},[72,1705,1706,1709,1711,1714,1716,1718,1720,1722,1725,1727],{"class":74,"line":127},[72,1707,1708],{"class":86},"gradient",[72,1710,172],{"class":82},[72,1712,1713],{"class":150},"addColorStop",[72,1715,457],{"class":86},[72,1717,1530],{"class":236},[72,1719,201],{"class":82},[72,1721,96],{"class":82},[72,1723,1724],{"class":99},"rgba(0, 0, 0, 1)",[72,1726,298],{"class":82},[72,1728,390],{"class":86},[72,1730,1731,1733,1735,1737,1739,1741,1743,1745,1748,1750],{"class":74,"line":134},[72,1732,1708],{"class":86},[72,1734,172],{"class":82},[72,1736,1713],{"class":150},[72,1738,457],{"class":86},[72,1740,1548],{"class":236},[72,1742,201],{"class":82},[72,1744,96],{"class":82},[72,1746,1747],{"class":99},"rgba(0, 0, 0, 0)",[72,1749,298],{"class":82},[72,1751,390],{"class":86},[18,1753,1754],{},"每个 GeoJSON 点绘制到灰度密度图时，会先按最大权重归一化：",[63,1756,1758],{"className":65,"code":1757,"language":67,"meta":68,"style":68},"const projected = projectLngLatToHeatmap(point.longitude, point.latitude, bounds, size.width, size.height)\nconst normalizedValue = point.value \u002F maxValue\n\ncontext.globalAlpha = clamp(normalizedValue, 0.02, 1)\ncontext.drawImage(\n  sprite,\n  projected.x - heatmapOptions.radius,\n  projected.y - heatmapOptions.radius\n)\n",[29,1759,1760,1814,1836,1840,1869,1881,1888,1910,1928],{"__ignoreMap":68},[72,1761,1762,1764,1767,1769,1772,1775,1777,1780,1782,1785,1787,1790,1792,1795,1797,1800,1802,1805,1807,1809,1811],{"class":74,"line":75},[72,1763,138],{"class":137},[72,1765,1766],{"class":86}," projected ",[72,1768,144],{"class":82},[72,1770,1771],{"class":150}," projectLngLatToHeatmap",[72,1773,1774],{"class":86},"(point",[72,1776,172],{"class":82},[72,1778,1779],{"class":86},"longitude",[72,1781,201],{"class":82},[72,1783,1784],{"class":86}," point",[72,1786,172],{"class":82},[72,1788,1789],{"class":86},"latitude",[72,1791,201],{"class":82},[72,1793,1794],{"class":86}," bounds",[72,1796,201],{"class":82},[72,1798,1799],{"class":86}," size",[72,1801,172],{"class":82},[72,1803,1804],{"class":86},"width",[72,1806,201],{"class":82},[72,1808,1799],{"class":86},[72,1810,172],{"class":82},[72,1812,1813],{"class":86},"height)\n",[72,1815,1816,1818,1821,1823,1825,1827,1830,1833],{"class":74,"line":106},[72,1817,138],{"class":137},[72,1819,1820],{"class":86}," normalizedValue ",[72,1822,144],{"class":82},[72,1824,1784],{"class":86},[72,1826,172],{"class":82},[72,1828,1829],{"class":86},"value ",[72,1831,1832],{"class":82},"\u002F",[72,1834,1835],{"class":86}," maxValue\n",[72,1837,1838],{"class":74,"line":127},[72,1839,131],{"emptyLinePlaceholder":130},[72,1841,1842,1845,1847,1850,1852,1854,1857,1859,1862,1864,1867],{"class":74,"line":134},[72,1843,1844],{"class":86},"context",[72,1846,172],{"class":82},[72,1848,1849],{"class":86},"globalAlpha ",[72,1851,144],{"class":82},[72,1853,1643],{"class":150},[72,1855,1856],{"class":86},"(normalizedValue",[72,1858,201],{"class":82},[72,1860,1861],{"class":236}," 0.02",[72,1863,201],{"class":82},[72,1865,1866],{"class":236}," 1",[72,1868,390],{"class":86},[72,1870,1871,1873,1875,1878],{"class":74,"line":156},[72,1872,1844],{"class":86},[72,1874,172],{"class":82},[72,1876,1877],{"class":150},"drawImage",[72,1879,1880],{"class":86},"(\n",[72,1882,1883,1886],{"class":74,"line":181},[72,1884,1885],{"class":86},"  sprite",[72,1887,213],{"class":82},[72,1889,1890,1893,1895,1898,1900,1903,1905,1908],{"class":74,"line":186},[72,1891,1892],{"class":86},"  projected",[72,1894,172],{"class":82},[72,1896,1897],{"class":86},"x ",[72,1899,1649],{"class":82},[72,1901,1902],{"class":86}," heatmapOptions",[72,1904,172],{"class":82},[72,1906,1907],{"class":86},"radius",[72,1909,213],{"class":82},[72,1911,1912,1914,1916,1919,1921,1923,1925],{"class":74,"line":207},[72,1913,1892],{"class":86},[72,1915,172],{"class":82},[72,1917,1918],{"class":86},"y ",[72,1920,1649],{"class":82},[72,1922,1902],{"class":86},[72,1924,172],{"class":82},[72,1926,1927],{"class":86},"radius\n",[72,1929,1930],{"class":74,"line":216},[72,1931,390],{"class":86},[18,1933,1934],{},"最后再把 alpha 转成彩色热力图：",[63,1936,1938],{"className":65,"code":1937,"language":67,"meta":68,"style":68},"const intensity = pixels[index + 3] \u002F 255\nconst colorIndex = clampInteger(intensity * 255, 0, 255) * 4\nconst opacity = minOpacity + intensity * (maxOpacity - minOpacity)\n\npixels[index] = colorRamp[colorIndex]\npixels[index + 1] = colorRamp[colorIndex + 1]\npixels[index + 2] = colorRamp[colorIndex + 2]\npixels[index + 3] = clampInteger(opacity * 255, 0, 255)\n",[29,1939,1940,1966,2002,2028,2032,2042,2064,2085],{"__ignoreMap":68},[72,1941,1942,1944,1947,1949,1952,1955,1958,1961,1963],{"class":74,"line":75},[72,1943,138],{"class":137},[72,1945,1946],{"class":86}," intensity ",[72,1948,144],{"class":82},[72,1950,1951],{"class":86}," pixels[index ",[72,1953,1954],{"class":82},"+",[72,1956,1957],{"class":236}," 3",[72,1959,1960],{"class":86},"] ",[72,1962,1832],{"class":82},[72,1964,1965],{"class":236}," 255\n",[72,1967,1968,1970,1973,1975,1978,1981,1984,1987,1989,1991,1993,1995,1997,1999],{"class":74,"line":106},[72,1969,138],{"class":137},[72,1971,1972],{"class":86}," colorIndex ",[72,1974,144],{"class":82},[72,1976,1977],{"class":150}," clampInteger",[72,1979,1980],{"class":86},"(intensity ",[72,1982,1983],{"class":82},"*",[72,1985,1986],{"class":236}," 255",[72,1988,201],{"class":82},[72,1990,1574],{"class":236},[72,1992,201],{"class":82},[72,1994,1986],{"class":236},[72,1996,748],{"class":86},[72,1998,1983],{"class":82},[72,2000,2001],{"class":236}," 4\n",[72,2003,2004,2006,2009,2011,2014,2016,2018,2020,2023,2025],{"class":74,"line":127},[72,2005,138],{"class":137},[72,2007,2008],{"class":86}," opacity ",[72,2010,144],{"class":82},[72,2012,2013],{"class":86}," minOpacity ",[72,2015,1954],{"class":82},[72,2017,1946],{"class":86},[72,2019,1983],{"class":82},[72,2021,2022],{"class":86}," (maxOpacity ",[72,2024,1649],{"class":82},[72,2026,2027],{"class":86}," minOpacity)\n",[72,2029,2030],{"class":74,"line":134},[72,2031,131],{"emptyLinePlaceholder":130},[72,2033,2034,2037,2039],{"class":74,"line":156},[72,2035,2036],{"class":86},"pixels[index] ",[72,2038,144],{"class":82},[72,2040,2041],{"class":86}," colorRamp[colorIndex]\n",[72,2043,2044,2047,2049,2051,2053,2055,2058,2060,2062],{"class":74,"line":181},[72,2045,2046],{"class":86},"pixels[index ",[72,2048,1954],{"class":82},[72,2050,1866],{"class":236},[72,2052,1960],{"class":86},[72,2054,144],{"class":82},[72,2056,2057],{"class":86}," colorRamp[colorIndex ",[72,2059,1954],{"class":82},[72,2061,1866],{"class":236},[72,2063,1358],{"class":86},[72,2065,2066,2068,2070,2073,2075,2077,2079,2081,2083],{"class":74,"line":186},[72,2067,2046],{"class":86},[72,2069,1954],{"class":82},[72,2071,2072],{"class":236}," 2",[72,2074,1960],{"class":86},[72,2076,144],{"class":82},[72,2078,2057],{"class":86},[72,2080,1954],{"class":82},[72,2082,2072],{"class":236},[72,2084,1358],{"class":86},[72,2086,2087,2089,2091,2093,2095,2097,2099,2102,2104,2106,2108,2110,2112,2114],{"class":74,"line":207},[72,2088,2046],{"class":86},[72,2090,1954],{"class":82},[72,2092,1957],{"class":236},[72,2094,1960],{"class":86},[72,2096,144],{"class":82},[72,2098,1977],{"class":150},[72,2100,2101],{"class":86},"(opacity ",[72,2103,1983],{"class":82},[72,2105,1986],{"class":236},[72,2107,201],{"class":82},[72,2109,1574],{"class":236},[72,2111,201],{"class":82},[72,2113,1986],{"class":236},[72,2115,390],{"class":86},[18,2117,2118,2119,2122,2123,2126],{},"所以 ",[29,2120,2121],{},"minOpacity"," 和 ",[29,2124,2125],{},"maxOpacity"," 不是简单设置整个图层透明度，而是在每个像素颜色映射阶段参与计算。",[14,2128,2130],{"id":2129},"groundprimitive-怎么绑定热力图","GroundPrimitive 怎么绑定热力图",[18,2132,2133,2134,2137,2138,413],{},"这次改造的核心，就是把原来的 ",[29,2135,2136],{},"SingleTileImageryProvider"," 改成 ",[29,2139,44],{},[18,2141,2142],{},"首先会做能力检查：",[63,2144,2146],{"className":65,"code":2145,"language":67,"meta":68,"style":68},"function ensureGroundPrimitiveSupport(Cesium, viewer) {\n  if (!Cesium.GroundPrimitive.isSupported(viewer.scene)) {\n    throw new Error('当前环境不支持 GroundPrimitive，无法渲染热力图。')\n  }\n\n  if (!Cesium.GroundPrimitive.supportsMaterials(viewer.scene)) {\n    throw new Error('当前环境不支持 GroundPrimitive 材质，无法渲染热力图。')\n  }\n}\n",[29,2147,2148,2168,2202,2223,2227,2231,2262,2281,2285],{"__ignoreMap":68},[72,2149,2150,2152,2155,2157,2159,2161,2164,2166],{"class":74,"line":75},[72,2151,909],{"class":137},[72,2153,2154],{"class":150}," ensureGroundPrimitiveSupport",[72,2156,457],{"class":82},[72,2158,401],{"class":787},[72,2160,201],{"class":82},[72,2162,2163],{"class":787}," viewer",[72,2165,791],{"class":82},[72,2167,204],{"class":82},[72,2169,2170,2172,2174,2176,2178,2180,2182,2184,2187,2189,2192,2194,2197,2200],{"class":74,"line":106},[72,2171,736],{"class":78},[72,2173,739],{"class":219},[72,2175,742],{"class":82},[72,2177,401],{"class":86},[72,2179,172],{"class":82},[72,2181,44],{"class":86},[72,2183,172],{"class":82},[72,2185,2186],{"class":150},"isSupported",[72,2188,457],{"class":219},[72,2190,2191],{"class":86},"viewer",[72,2193,172],{"class":82},[72,2195,2196],{"class":86},"scene",[72,2198,2199],{"class":219},")) ",[72,2201,460],{"class":82},[72,2203,2204,2207,2209,2212,2214,2216,2219,2221],{"class":74,"line":127},[72,2205,2206],{"class":78},"    throw",[72,2208,166],{"class":82},[72,2210,2211],{"class":150}," Error",[72,2213,457],{"class":219},[72,2215,298],{"class":82},[72,2217,2218],{"class":99},"当前环境不支持 GroundPrimitive，无法渲染热力图。",[72,2220,298],{"class":82},[72,2222,390],{"class":219},[72,2224,2225],{"class":74,"line":134},[72,2226,868],{"class":82},[72,2228,2229],{"class":74,"line":156},[72,2230,131],{"emptyLinePlaceholder":130},[72,2232,2233,2235,2237,2239,2241,2243,2245,2247,2250,2252,2254,2256,2258,2260],{"class":74,"line":181},[72,2234,736],{"class":78},[72,2236,739],{"class":219},[72,2238,742],{"class":82},[72,2240,401],{"class":86},[72,2242,172],{"class":82},[72,2244,44],{"class":86},[72,2246,172],{"class":82},[72,2248,2249],{"class":150},"supportsMaterials",[72,2251,457],{"class":219},[72,2253,2191],{"class":86},[72,2255,172],{"class":82},[72,2257,2196],{"class":86},[72,2259,2199],{"class":219},[72,2261,460],{"class":82},[72,2263,2264,2266,2268,2270,2272,2274,2277,2279],{"class":74,"line":186},[72,2265,2206],{"class":78},[72,2267,166],{"class":82},[72,2269,2211],{"class":150},[72,2271,457],{"class":219},[72,2273,298],{"class":82},[72,2275,2276],{"class":99},"当前环境不支持 GroundPrimitive 材质，无法渲染热力图。",[72,2278,298],{"class":82},[72,2280,390],{"class":219},[72,2282,2283],{"class":74,"line":207},[72,2284,868],{"class":82},[72,2286,2287],{"class":74,"line":216},[72,2288,885],{"class":82},[18,2290,2291],{},"然后把 canvas 纹理包装成 Image material：",[63,2293,2295],{"className":65,"code":2294,"language":67,"meta":68,"style":68},"function createGroundMaterial(Cesium, canvas) {\n  return Cesium.Material.fromType('Image', {\n    image: canvas,\n    transparent: true,\n    repeat: new Cesium.Cartesian2(1, 1),\n    color: Cesium.Color.WHITE\n  })\n}\n",[29,2296,2297,2317,2346,2357,2368,2396,2415,2421],{"__ignoreMap":68},[72,2298,2299,2301,2304,2306,2308,2310,2313,2315],{"class":74,"line":75},[72,2300,909],{"class":137},[72,2302,2303],{"class":150}," createGroundMaterial",[72,2305,457],{"class":82},[72,2307,401],{"class":787},[72,2309,201],{"class":82},[72,2311,2312],{"class":787}," canvas",[72,2314,791],{"class":82},[72,2316,204],{"class":82},[72,2318,2319,2321,2323,2325,2328,2330,2333,2335,2337,2340,2342,2344],{"class":74,"line":106},[72,2320,877],{"class":78},[72,2322,169],{"class":86},[72,2324,172],{"class":82},[72,2326,2327],{"class":86},"Material",[72,2329,172],{"class":82},[72,2331,2332],{"class":150},"fromType",[72,2334,457],{"class":219},[72,2336,298],{"class":82},[72,2338,2339],{"class":99},"Image",[72,2341,298],{"class":82},[72,2343,201],{"class":82},[72,2345,204],{"class":82},[72,2347,2348,2351,2353,2355],{"class":74,"line":127},[72,2349,2350],{"class":219},"    image",[72,2352,223],{"class":82},[72,2354,2312],{"class":86},[72,2356,213],{"class":82},[72,2358,2359,2362,2364,2366],{"class":74,"line":134},[72,2360,2361],{"class":219},"    transparent",[72,2363,223],{"class":82},[72,2365,364],{"class":363},[72,2367,213],{"class":82},[72,2369,2370,2373,2375,2377,2379,2381,2384,2386,2388,2390,2392,2394],{"class":74,"line":156},[72,2371,2372],{"class":219},"    repeat",[72,2374,223],{"class":82},[72,2376,166],{"class":82},[72,2378,169],{"class":86},[72,2380,172],{"class":82},[72,2382,2383],{"class":150},"Cartesian2",[72,2385,457],{"class":219},[72,2387,1548],{"class":236},[72,2389,201],{"class":82},[72,2391,1866],{"class":236},[72,2393,791],{"class":219},[72,2395,213],{"class":82},[72,2397,2398,2401,2403,2405,2407,2410,2412],{"class":74,"line":181},[72,2399,2400],{"class":219},"    color",[72,2402,223],{"class":82},[72,2404,169],{"class":86},[72,2406,172],{"class":82},[72,2408,2409],{"class":86},"Color",[72,2411,172],{"class":82},[72,2413,2414],{"class":86},"WHITE\n",[72,2416,2417,2419],{"class":74,"line":186},[72,2418,1113],{"class":82},[72,2420,390],{"class":219},[72,2422,2423],{"class":74,"line":207},[72,2424,885],{"class":82},[18,2426,2427],{},"真正创建 primitive 的逻辑如下：",[63,2429,2431],{"className":65,"code":2430,"language":67,"meta":68,"style":68},"function createGroundPrimitive(Cesium, state) {\n  const material = createGroundMaterial(Cesium, state.renderResult.canvas)\n  const appearance = new Cesium.EllipsoidSurfaceAppearance({\n    material,\n    translucent: true,\n    aboveGround: false\n  })\n  const primitive = new Cesium.GroundPrimitive({\n    geometryInstances: new Cesium.GeometryInstance({\n      geometry: new Cesium.RectangleGeometry({\n        rectangle: createCesiumRectangle(Cesium, state.bounds),\n        vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT\n      }),\n      id: 'geojson-heatmap-ground-primitive'\n    }),\n    appearance,\n    classificationType: toClassificationType(Cesium, state.classificationTarget)\n  })\n\n  primitive.show = state.visible\n\n  return {\n    primitive,\n    material,\n    appearance\n  }\n}\n",[29,2432,2433,2453,2485,2507,2514,2525,2535,2541,2562,2582,2602,2628,2646,2654,2668,2677,2684,2708,2714,2718,2737,2741,2747,2755,2762,2768,2773],{"__ignoreMap":68},[72,2434,2435,2437,2440,2442,2444,2446,2449,2451],{"class":74,"line":75},[72,2436,909],{"class":137},[72,2438,2439],{"class":150}," createGroundPrimitive",[72,2441,457],{"class":82},[72,2443,401],{"class":787},[72,2445,201],{"class":82},[72,2447,2448],{"class":787}," state",[72,2450,791],{"class":82},[72,2452,204],{"class":82},[72,2454,2455,2458,2461,2463,2465,2467,2469,2471,2473,2475,2478,2480,2483],{"class":74,"line":106},[72,2456,2457],{"class":137},"  const",[72,2459,2460],{"class":86}," material",[72,2462,758],{"class":82},[72,2464,2303],{"class":150},[72,2466,457],{"class":219},[72,2468,401],{"class":86},[72,2470,201],{"class":82},[72,2472,2448],{"class":86},[72,2474,172],{"class":82},[72,2476,2477],{"class":86},"renderResult",[72,2479,172],{"class":82},[72,2481,2482],{"class":86},"canvas",[72,2484,390],{"class":219},[72,2486,2487,2489,2492,2494,2496,2498,2500,2503,2505],{"class":74,"line":127},[72,2488,2457],{"class":137},[72,2490,2491],{"class":86}," appearance",[72,2493,758],{"class":82},[72,2495,166],{"class":82},[72,2497,169],{"class":86},[72,2499,172],{"class":82},[72,2501,2502],{"class":150},"EllipsoidSurfaceAppearance",[72,2504,457],{"class":219},[72,2506,460],{"class":82},[72,2508,2509,2512],{"class":74,"line":134},[72,2510,2511],{"class":86},"    material",[72,2513,213],{"class":82},[72,2515,2516,2519,2521,2523],{"class":74,"line":156},[72,2517,2518],{"class":219},"    translucent",[72,2520,223],{"class":82},[72,2522,364],{"class":363},[72,2524,213],{"class":82},[72,2526,2527,2530,2532],{"class":74,"line":181},[72,2528,2529],{"class":219},"    aboveGround",[72,2531,223],{"class":82},[72,2533,2534],{"class":363}," false\n",[72,2536,2537,2539],{"class":74,"line":186},[72,2538,1113],{"class":82},[72,2540,390],{"class":219},[72,2542,2543,2545,2548,2550,2552,2554,2556,2558,2560],{"class":74,"line":207},[72,2544,2457],{"class":137},[72,2546,2547],{"class":86}," primitive",[72,2549,758],{"class":82},[72,2551,166],{"class":82},[72,2553,169],{"class":86},[72,2555,172],{"class":82},[72,2557,44],{"class":150},[72,2559,457],{"class":219},[72,2561,460],{"class":82},[72,2563,2564,2567,2569,2571,2573,2575,2578,2580],{"class":74,"line":216},[72,2565,2566],{"class":219},"    geometryInstances",[72,2568,223],{"class":82},[72,2570,166],{"class":82},[72,2572,169],{"class":86},[72,2574,172],{"class":82},[72,2576,2577],{"class":150},"GeometryInstance",[72,2579,457],{"class":219},[72,2581,460],{"class":82},[72,2583,2584,2587,2589,2591,2593,2595,2598,2600],{"class":74,"line":228},[72,2585,2586],{"class":219},"      geometry",[72,2588,223],{"class":82},[72,2590,166],{"class":82},[72,2592,169],{"class":86},[72,2594,172],{"class":82},[72,2596,2597],{"class":150},"RectangleGeometry",[72,2599,457],{"class":219},[72,2601,460],{"class":82},[72,2603,2604,2607,2609,2612,2614,2616,2618,2620,2622,2624,2626],{"class":74,"line":242},[72,2605,2606],{"class":219},"        rectangle",[72,2608,223],{"class":82},[72,2610,2611],{"class":150}," createCesiumRectangle",[72,2613,457],{"class":219},[72,2615,401],{"class":86},[72,2617,201],{"class":82},[72,2619,2448],{"class":86},[72,2621,172],{"class":82},[72,2623,408],{"class":86},[72,2625,791],{"class":219},[72,2627,213],{"class":82},[72,2629,2630,2633,2635,2637,2639,2641,2643],{"class":74,"line":255},[72,2631,2632],{"class":219},"        vertexFormat",[72,2634,223],{"class":82},[72,2636,169],{"class":86},[72,2638,172],{"class":82},[72,2640,2502],{"class":86},[72,2642,172],{"class":82},[72,2644,2645],{"class":86},"VERTEX_FORMAT\n",[72,2647,2648,2650,2652],{"class":74,"line":268},[72,2649,818],{"class":82},[72,2651,791],{"class":219},[72,2653,213],{"class":82},[72,2655,2656,2659,2661,2663,2666],{"class":74,"line":279},[72,2657,2658],{"class":219},"      id",[72,2660,223],{"class":82},[72,2662,96],{"class":82},[72,2664,2665],{"class":99},"geojson-heatmap-ground-primitive",[72,2667,103],{"class":82},[72,2669,2670,2673,2675],{"class":74,"line":285},[72,2671,2672],{"class":82},"    }",[72,2674,791],{"class":219},[72,2676,213],{"class":82},[72,2678,2679,2682],{"class":74,"line":303},[72,2680,2681],{"class":86},"    appearance",[72,2683,213],{"class":82},[72,2685,2686,2689,2691,2694,2696,2698,2700,2702,2704,2706],{"class":74,"line":316},[72,2687,2688],{"class":219},"    classificationType",[72,2690,223],{"class":82},[72,2692,2693],{"class":150}," toClassificationType",[72,2695,457],{"class":219},[72,2697,401],{"class":86},[72,2699,201],{"class":82},[72,2701,2448],{"class":86},[72,2703,172],{"class":82},[72,2705,619],{"class":86},[72,2707,390],{"class":219},[72,2709,2710,2712],{"class":74,"line":329},[72,2711,1113],{"class":82},[72,2713,390],{"class":219},[72,2715,2716],{"class":74,"line":342},[72,2717,131],{"emptyLinePlaceholder":130},[72,2719,2720,2723,2725,2728,2730,2732,2734],{"class":74,"line":355},[72,2721,2722],{"class":86},"  primitive",[72,2724,172],{"class":82},[72,2726,2727],{"class":86},"show",[72,2729,758],{"class":82},[72,2731,2448],{"class":86},[72,2733,172],{"class":82},[72,2735,2736],{"class":86},"visible\n",[72,2738,2739],{"class":74,"line":369},[72,2740,131],{"emptyLinePlaceholder":130},[72,2742,2743,2745],{"class":74,"line":384},[72,2744,877],{"class":78},[72,2746,204],{"class":82},[72,2748,2750,2753],{"class":74,"line":2749},23,[72,2751,2752],{"class":86},"    primitive",[72,2754,213],{"class":82},[72,2756,2758,2760],{"class":74,"line":2757},24,[72,2759,2511],{"class":86},[72,2761,213],{"class":82},[72,2763,2765],{"class":74,"line":2764},25,[72,2766,2767],{"class":86},"    appearance\n",[72,2769,2771],{"class":74,"line":2770},26,[72,2772,868],{"class":82},[72,2774,2776],{"class":74,"line":2775},27,[72,2777,885],{"class":82},[18,2779,2780],{},"这里的职责边界很清楚：",[22,2782,2783,2788,2794],{},[25,2784,2785,2787],{},[29,2786,2597],{}," 定义热力图的经纬度覆盖范围",[25,2789,2790,2793],{},[29,2791,2792],{},"Image material"," 负责把 canvas 结果变成可采样纹理",[25,2795,2796,2798],{},[29,2797,44],{}," 负责把这张热力图分类到 Cesium 场景表面",[14,2800,2802],{"id":2801},"demo-怎么补地形和-3d-tiles-测试场景","Demo 怎么补地形和 3D Tiles 测试场景",[18,2804,2805],{},"为了验证热力图是否真的同时贴到地形和模型，Demo runtime 会额外加载两层场景资源：",[581,2807,2808,2811],{},[25,2809,2810],{},"Cesium World Terrain",[25,2812,2813],{},"Cesium OSM Buildings",[18,2815,2816,2817,662],{},"加载逻辑集中在 ",[29,2818,2819],{},"loadTerrainAndTiles()",[63,2821,2823],{"className":65,"code":2822,"language":67,"meta":68,"style":68},"async function loadTerrainAndTiles(Cesium, viewer) {\n  const results = await Promise.allSettled([\n    Cesium.createWorldTerrainAsync({\n      requestWaterMask: true,\n      requestVertexNormals: true\n    }),\n    Cesium.createOsmBuildingsAsync({\n      defaultColor: Cesium.Color.fromCssColorString('#d9e0ea').withAlpha(0.92),\n      enableShowOutline: false,\n      showOutline: false\n    })\n  ])\n",[29,2824,2825,2847,2869,2883,2894,2904,2912,2925,2968,2980,2989,2995],{"__ignoreMap":68},[72,2826,2827,2830,2832,2835,2837,2839,2841,2843,2845],{"class":74,"line":75},[72,2828,2829],{"class":137},"async",[72,2831,706],{"class":137},[72,2833,2834],{"class":150}," loadTerrainAndTiles",[72,2836,457],{"class":82},[72,2838,401],{"class":787},[72,2840,201],{"class":82},[72,2842,2163],{"class":787},[72,2844,791],{"class":82},[72,2846,204],{"class":82},[72,2848,2849,2851,2854,2856,2858,2861,2863,2866],{"class":74,"line":106},[72,2850,2457],{"class":137},[72,2852,2853],{"class":86}," results",[72,2855,758],{"class":82},[72,2857,147],{"class":78},[72,2859,2860],{"class":1196}," Promise",[72,2862,172],{"class":82},[72,2864,2865],{"class":150},"allSettled",[72,2867,2868],{"class":219},"([\n",[72,2870,2871,2874,2876,2879,2881],{"class":74,"line":127},[72,2872,2873],{"class":86},"    Cesium",[72,2875,172],{"class":82},[72,2877,2878],{"class":150},"createWorldTerrainAsync",[72,2880,457],{"class":219},[72,2882,460],{"class":82},[72,2884,2885,2888,2890,2892],{"class":74,"line":134},[72,2886,2887],{"class":219},"      requestWaterMask",[72,2889,223],{"class":82},[72,2891,364],{"class":363},[72,2893,213],{"class":82},[72,2895,2896,2899,2901],{"class":74,"line":156},[72,2897,2898],{"class":219},"      requestVertexNormals",[72,2900,223],{"class":82},[72,2902,2903],{"class":363}," true\n",[72,2905,2906,2908,2910],{"class":74,"line":181},[72,2907,2672],{"class":82},[72,2909,791],{"class":219},[72,2911,213],{"class":82},[72,2913,2914,2916,2918,2921,2923],{"class":74,"line":186},[72,2915,2873],{"class":86},[72,2917,172],{"class":82},[72,2919,2920],{"class":150},"createOsmBuildingsAsync",[72,2922,457],{"class":219},[72,2924,460],{"class":82},[72,2926,2927,2930,2932,2934,2936,2938,2940,2943,2945,2947,2950,2952,2954,2956,2959,2961,2964,2966],{"class":74,"line":207},[72,2928,2929],{"class":219},"      defaultColor",[72,2931,223],{"class":82},[72,2933,169],{"class":86},[72,2935,172],{"class":82},[72,2937,2409],{"class":86},[72,2939,172],{"class":82},[72,2941,2942],{"class":150},"fromCssColorString",[72,2944,457],{"class":219},[72,2946,298],{"class":82},[72,2948,2949],{"class":99},"#d9e0ea",[72,2951,298],{"class":82},[72,2953,791],{"class":219},[72,2955,172],{"class":82},[72,2957,2958],{"class":150},"withAlpha",[72,2960,457],{"class":219},[72,2962,2963],{"class":236},"0.92",[72,2965,791],{"class":219},[72,2967,213],{"class":82},[72,2969,2970,2973,2975,2978],{"class":74,"line":216},[72,2971,2972],{"class":219},"      enableShowOutline",[72,2974,223],{"class":82},[72,2976,2977],{"class":363}," false",[72,2979,213],{"class":82},[72,2981,2982,2985,2987],{"class":74,"line":228},[72,2983,2984],{"class":219},"      showOutline",[72,2986,223],{"class":82},[72,2988,2534],{"class":363},[72,2990,2991,2993],{"class":74,"line":242},[72,2992,2672],{"class":82},[72,2994,390],{"class":219},[72,2996,2997],{"class":74,"line":255},[72,2998,2999],{"class":219},"  ])\n",[18,3001,3002,3003,662],{},"地形成功后，直接挂到 ",[29,3004,3005],{},"viewer.terrainProvider",[63,3007,3009],{"className":65,"code":3008,"language":67,"meta":68,"style":68},"if (terrainResult?.status === 'fulfilled') {\n  viewer.terrainProvider = terrainResult.value\n  sceneState.terrainReady = true\n}\n",[29,3010,3011,3036,3056,3070],{"__ignoreMap":68},[72,3012,3013,3015,3018,3020,3023,3025,3027,3030,3032,3034],{"class":74,"line":75},[72,3014,1503],{"class":78},[72,3016,3017],{"class":86}," (terrainResult",[72,3019,972],{"class":82},[72,3021,3022],{"class":86},"status ",[72,3024,1434],{"class":82},[72,3026,96],{"class":82},[72,3028,3029],{"class":99},"fulfilled",[72,3031,298],{"class":82},[72,3033,748],{"class":86},[72,3035,460],{"class":82},[72,3037,3038,3041,3043,3046,3048,3051,3053],{"class":74,"line":106},[72,3039,3040],{"class":86},"  viewer",[72,3042,172],{"class":82},[72,3044,3045],{"class":86},"terrainProvider",[72,3047,758],{"class":82},[72,3049,3050],{"class":86}," terrainResult",[72,3052,172],{"class":82},[72,3054,3055],{"class":86},"value\n",[72,3057,3058,3061,3063,3066,3068],{"class":74,"line":127},[72,3059,3060],{"class":86},"  sceneState",[72,3062,172],{"class":82},[72,3064,3065],{"class":86},"terrainReady",[72,3067,758],{"class":82},[72,3069,2903],{"class":363},[72,3071,3072],{"class":74,"line":134},[72,3073,885],{"class":82},[18,3075,3076,3077,662],{},"OSM Buildings 成功后，直接加到 ",[29,3078,3079],{},"viewer.scene.primitives",[63,3081,3083],{"className":65,"code":3082,"language":67,"meta":68,"style":68},"if (tilesResult?.status === 'fulfilled') {\n  sceneState.tileset = viewer.scene.primitives.add(tilesResult.value)\n  sceneState.tilesReady = true\n}\n",[29,3084,3085,3108,3146,3159],{"__ignoreMap":68},[72,3086,3087,3089,3092,3094,3096,3098,3100,3102,3104,3106],{"class":74,"line":75},[72,3088,1503],{"class":78},[72,3090,3091],{"class":86}," (tilesResult",[72,3093,972],{"class":82},[72,3095,3022],{"class":86},[72,3097,1434],{"class":82},[72,3099,96],{"class":82},[72,3101,3029],{"class":99},[72,3103,298],{"class":82},[72,3105,748],{"class":86},[72,3107,460],{"class":82},[72,3109,3110,3112,3114,3117,3119,3121,3123,3125,3127,3130,3132,3135,3137,3140,3142,3144],{"class":74,"line":106},[72,3111,3060],{"class":86},[72,3113,172],{"class":82},[72,3115,3116],{"class":86},"tileset",[72,3118,758],{"class":82},[72,3120,2163],{"class":86},[72,3122,172],{"class":82},[72,3124,2196],{"class":86},[72,3126,172],{"class":82},[72,3128,3129],{"class":86},"primitives",[72,3131,172],{"class":82},[72,3133,3134],{"class":150},"add",[72,3136,457],{"class":219},[72,3138,3139],{"class":86},"tilesResult",[72,3141,172],{"class":82},[72,3143,295],{"class":86},[72,3145,390],{"class":219},[72,3147,3148,3150,3152,3155,3157],{"class":74,"line":127},[72,3149,3060],{"class":86},[72,3151,172],{"class":82},[72,3153,3154],{"class":86},"tilesReady",[72,3156,758],{"class":82},[72,3158,2903],{"class":363},[72,3160,3161],{"class":74,"line":134},[72,3162,885],{"class":82},[18,3164,3165,3166,3169,3170,3173],{},"这里不用把热力图和 tileset 再做额外绑定，因为当前热力图本身就是 ",[29,3167,3168],{},"GroundPrimitive classification","。只要 tileset 已经进入场景，",[29,3171,3172],{},"classificationTarget = 'tiles' | 'both'"," 就会直接作用到模型表面。",[14,3175,3176],{"id":3176},"为什么要选一个热力图测试点",[18,3178,3179],{},"只把地形和 3D Tiles 加进场景还不够，观察上也需要一个固定锚点。Demo 现在会从当前 GeoJSON 中选一个离参考城市点最近的真实热力点，作为“热力图测试点”：",[63,3181,3183],{"className":65,"code":3182,"language":67,"meta":68,"style":68},"const HEATMAP_TEST_POINT_REFERENCE = Object.freeze({\n  longitude: 114.059,\n  latitude: 22.543,\n  name: '热力图测试点'\n})\n",[29,3184,3185,3206,3218,3230,3244],{"__ignoreMap":68},[72,3186,3187,3189,3192,3194,3197,3199,3202,3204],{"class":74,"line":75},[72,3188,138],{"class":137},[72,3190,3191],{"class":86}," HEATMAP_TEST_POINT_REFERENCE ",[72,3193,144],{"class":82},[72,3195,3196],{"class":86}," Object",[72,3198,172],{"class":82},[72,3200,3201],{"class":150},"freeze",[72,3203,457],{"class":86},[72,3205,460],{"class":82},[72,3207,3208,3211,3213,3216],{"class":74,"line":106},[72,3209,3210],{"class":219},"  longitude",[72,3212,223],{"class":82},[72,3214,3215],{"class":236}," 114.059",[72,3217,213],{"class":82},[72,3219,3220,3223,3225,3228],{"class":74,"line":127},[72,3221,3222],{"class":219},"  latitude",[72,3224,223],{"class":82},[72,3226,3227],{"class":236}," 22.543",[72,3229,213],{"class":82},[72,3231,3232,3235,3237,3239,3242],{"class":74,"line":134},[72,3233,3234],{"class":219},"  name",[72,3236,223],{"class":82},[72,3238,96],{"class":82},[72,3240,3241],{"class":99},"热力图测试点",[72,3243,103],{"class":82},[72,3245,3246,3248],{"class":74,"line":156},[72,3247,387],{"class":82},[72,3249,390],{"class":86},[18,3251,3252],{},"然后从当前热力图点集中挑选最适合观察的点：",[63,3254,3256],{"className":65,"code":3255,"language":67,"meta":68,"style":68},"function findHeatmapTestPoint(featureCollection) {\n  const features = Array.isArray(featureCollection?.features) ? featureCollection.features : []\n  let bestPoint = null\n  let bestScore = Number.POSITIVE_INFINITY\n\n  for (const feature of features) {\n    const point = getFeaturePoint(feature)\n\n    if (!point) {\n      continue\n    }\n\n    const longitudeDelta = point.longitude - HEATMAP_TEST_POINT_REFERENCE.longitude\n    const latitudeDelta = point.latitude - HEATMAP_TEST_POINT_REFERENCE.latitude\n    const distanceScore = longitudeDelta * longitudeDelta + latitudeDelta * latitudeDelta\n    const valueBonus = point.value \u002F 100000\n    const score = distanceScore - valueBonus\n",[29,3257,3258,3274,3316,3328,3345,3349,3369,3387,3391,3406,3411,3415,3419,3445,3469,3495,3516],{"__ignoreMap":68},[72,3259,3260,3262,3265,3267,3270,3272],{"class":74,"line":75},[72,3261,909],{"class":137},[72,3263,3264],{"class":150}," findHeatmapTestPoint",[72,3266,457],{"class":82},[72,3268,3269],{"class":787},"featureCollection",[72,3271,791],{"class":82},[72,3273,204],{"class":82},[72,3275,3276,3278,3281,3283,3286,3288,3290,3292,3294,3296,3298,3300,3303,3306,3308,3310,3313],{"class":74,"line":106},[72,3277,2457],{"class":137},[72,3279,3280],{"class":86}," features",[72,3282,758],{"class":82},[72,3284,3285],{"class":86}," Array",[72,3287,172],{"class":82},[72,3289,1472],{"class":150},[72,3291,457],{"class":219},[72,3293,3269],{"class":86},[72,3295,972],{"class":82},[72,3297,1176],{"class":86},[72,3299,748],{"class":219},[72,3301,3302],{"class":82},"?",[72,3304,3305],{"class":86}," featureCollection",[72,3307,172],{"class":82},[72,3309,1176],{"class":86},[72,3311,3312],{"class":82}," :",[72,3314,3315],{"class":219}," []\n",[72,3317,3318,3321,3324,3326],{"class":74,"line":127},[72,3319,3320],{"class":137},"  let",[72,3322,3323],{"class":86}," bestPoint",[72,3325,758],{"class":82},[72,3327,680],{"class":82},[72,3329,3330,3332,3335,3337,3340,3342],{"class":74,"line":134},[72,3331,3320],{"class":137},[72,3333,3334],{"class":86}," bestScore",[72,3336,758],{"class":82},[72,3338,3339],{"class":86}," Number",[72,3341,172],{"class":82},[72,3343,3344],{"class":219},"POSITIVE_INFINITY\n",[72,3346,3347],{"class":74,"line":156},[72,3348,131],{"emptyLinePlaceholder":130},[72,3350,3351,3354,3356,3358,3360,3363,3365,3367],{"class":74,"line":181},[72,3352,3353],{"class":78},"  for",[72,3355,739],{"class":219},[72,3357,138],{"class":137},[72,3359,1426],{"class":86},[72,3361,3362],{"class":82}," of",[72,3364,3280],{"class":86},[72,3366,748],{"class":219},[72,3368,460],{"class":82},[72,3370,3371,3373,3375,3377,3380,3382,3385],{"class":74,"line":186},[72,3372,1039],{"class":137},[72,3374,1784],{"class":86},[72,3376,758],{"class":82},[72,3378,3379],{"class":150}," getFeaturePoint",[72,3381,457],{"class":219},[72,3383,3384],{"class":86},"feature",[72,3386,390],{"class":219},[72,3388,3389],{"class":74,"line":207},[72,3390,131],{"emptyLinePlaceholder":130},[72,3392,3393,3395,3397,3399,3402,3404],{"class":74,"line":216},[72,3394,1057],{"class":78},[72,3396,739],{"class":219},[72,3398,742],{"class":82},[72,3400,3401],{"class":86},"point",[72,3403,748],{"class":219},[72,3405,460],{"class":82},[72,3407,3408],{"class":74,"line":228},[72,3409,3410],{"class":78},"      continue\n",[72,3412,3413],{"class":74,"line":242},[72,3414,1089],{"class":82},[72,3416,3417],{"class":74,"line":255},[72,3418,131],{"emptyLinePlaceholder":130},[72,3420,3421,3423,3426,3428,3430,3432,3434,3437,3440,3442],{"class":74,"line":268},[72,3422,1039],{"class":137},[72,3424,3425],{"class":86}," longitudeDelta",[72,3427,758],{"class":82},[72,3429,1784],{"class":86},[72,3431,172],{"class":82},[72,3433,1779],{"class":86},[72,3435,3436],{"class":82}," -",[72,3438,3439],{"class":86}," HEATMAP_TEST_POINT_REFERENCE",[72,3441,172],{"class":82},[72,3443,3444],{"class":86},"longitude\n",[72,3446,3447,3449,3452,3454,3456,3458,3460,3462,3464,3466],{"class":74,"line":279},[72,3448,1039],{"class":137},[72,3450,3451],{"class":86}," latitudeDelta",[72,3453,758],{"class":82},[72,3455,1784],{"class":86},[72,3457,172],{"class":82},[72,3459,1789],{"class":86},[72,3461,3436],{"class":82},[72,3463,3439],{"class":86},[72,3465,172],{"class":82},[72,3467,3468],{"class":86},"latitude\n",[72,3470,3471,3473,3476,3478,3480,3483,3485,3488,3490,3492],{"class":74,"line":285},[72,3472,1039],{"class":137},[72,3474,3475],{"class":86}," distanceScore",[72,3477,758],{"class":82},[72,3479,3425],{"class":86},[72,3481,3482],{"class":82}," *",[72,3484,3425],{"class":86},[72,3486,3487],{"class":82}," +",[72,3489,3451],{"class":86},[72,3491,3482],{"class":82},[72,3493,3494],{"class":86}," latitudeDelta\n",[72,3496,3497,3499,3502,3504,3506,3508,3510,3513],{"class":74,"line":303},[72,3498,1039],{"class":137},[72,3500,3501],{"class":86}," valueBonus",[72,3503,758],{"class":82},[72,3505,1784],{"class":86},[72,3507,172],{"class":82},[72,3509,295],{"class":86},[72,3511,3512],{"class":82}," \u002F",[72,3514,3515],{"class":236}," 100000\n",[72,3517,3518,3520,3523,3525,3527,3529],{"class":74,"line":316},[72,3519,1039],{"class":137},[72,3521,3522],{"class":86}," score",[72,3524,758],{"class":82},[72,3526,3475],{"class":86},[72,3528,3436],{"class":82},[72,3530,3531],{"class":86}," valueBonus\n",[18,3533,3534],{},"这个策略的目的不是找“全局最大值”，而是找“离城市高密区域更近、同时又确实属于当前热力图数据”的一个真实点。这样相机切过去时，更容易直接看到 OSM Buildings，也更容易观察热力图是否贴到了建筑表面。",[14,3536,3537],{"id":3537},"测试点怎么可视化和定位",[18,3539,3540],{},"选出测试点后，Demo 会往场景里加一个贴地 marker：",[63,3542,3544],{"className":65,"code":3543,"language":67,"meta":68,"style":68},"return viewer.entities.add({\n  position: Cesium.Cartesian3.fromDegrees(testPoint.longitude, testPoint.latitude, 0),\n  point: {\n    pixelSize: 12,\n    color: Cesium.Color.fromCssColorString('#fff173'),\n    outlineColor: Cesium.Color.fromCssColorString('#08111f'),\n    outlineWidth: 2,\n    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND\n  },\n  label: {\n    text: labelText,\n    showBackground: true,\n    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND\n  }\n})\n",[29,3545,3546,3566,3609,3618,3630,3659,3689,3700,3719,3723,3732,3744,3755,3771,3775],{"__ignoreMap":68},[72,3547,3548,3551,3553,3555,3558,3560,3562,3564],{"class":74,"line":75},[72,3549,3550],{"class":78},"return",[72,3552,2163],{"class":86},[72,3554,172],{"class":82},[72,3556,3557],{"class":86},"entities",[72,3559,172],{"class":82},[72,3561,3134],{"class":150},[72,3563,457],{"class":86},[72,3565,460],{"class":82},[72,3567,3568,3571,3573,3575,3577,3580,3582,3585,3588,3590,3592,3594,3597,3599,3601,3603,3605,3607],{"class":74,"line":106},[72,3569,3570],{"class":219},"  position",[72,3572,223],{"class":82},[72,3574,169],{"class":86},[72,3576,172],{"class":82},[72,3578,3579],{"class":86},"Cartesian3",[72,3581,172],{"class":82},[72,3583,3584],{"class":150},"fromDegrees",[72,3586,3587],{"class":86},"(testPoint",[72,3589,172],{"class":82},[72,3591,1779],{"class":86},[72,3593,201],{"class":82},[72,3595,3596],{"class":86}," testPoint",[72,3598,172],{"class":82},[72,3600,1789],{"class":86},[72,3602,201],{"class":82},[72,3604,1574],{"class":236},[72,3606,791],{"class":86},[72,3608,213],{"class":82},[72,3610,3611,3614,3616],{"class":74,"line":127},[72,3612,3613],{"class":219},"  point",[72,3615,223],{"class":82},[72,3617,204],{"class":82},[72,3619,3620,3623,3625,3628],{"class":74,"line":134},[72,3621,3622],{"class":219},"    pixelSize",[72,3624,223],{"class":82},[72,3626,3627],{"class":236}," 12",[72,3629,213],{"class":82},[72,3631,3632,3634,3636,3638,3640,3642,3644,3646,3648,3650,3653,3655,3657],{"class":74,"line":156},[72,3633,2400],{"class":219},[72,3635,223],{"class":82},[72,3637,169],{"class":86},[72,3639,172],{"class":82},[72,3641,2409],{"class":86},[72,3643,172],{"class":82},[72,3645,2942],{"class":150},[72,3647,457],{"class":86},[72,3649,298],{"class":82},[72,3651,3652],{"class":99},"#fff173",[72,3654,298],{"class":82},[72,3656,791],{"class":86},[72,3658,213],{"class":82},[72,3660,3661,3664,3666,3668,3670,3672,3674,3676,3678,3680,3683,3685,3687],{"class":74,"line":181},[72,3662,3663],{"class":219},"    outlineColor",[72,3665,223],{"class":82},[72,3667,169],{"class":86},[72,3669,172],{"class":82},[72,3671,2409],{"class":86},[72,3673,172],{"class":82},[72,3675,2942],{"class":150},[72,3677,457],{"class":86},[72,3679,298],{"class":82},[72,3681,3682],{"class":99},"#08111f",[72,3684,298],{"class":82},[72,3686,791],{"class":86},[72,3688,213],{"class":82},[72,3690,3691,3694,3696,3698],{"class":74,"line":186},[72,3692,3693],{"class":219},"    outlineWidth",[72,3695,223],{"class":82},[72,3697,2072],{"class":236},[72,3699,213],{"class":82},[72,3701,3702,3705,3707,3709,3711,3714,3716],{"class":74,"line":207},[72,3703,3704],{"class":219},"    heightReference",[72,3706,223],{"class":82},[72,3708,169],{"class":86},[72,3710,172],{"class":82},[72,3712,3713],{"class":86},"HeightReference",[72,3715,172],{"class":82},[72,3717,3718],{"class":86},"CLAMP_TO_GROUND\n",[72,3720,3721],{"class":74,"line":216},[72,3722,282],{"class":82},[72,3724,3725,3728,3730],{"class":74,"line":228},[72,3726,3727],{"class":219},"  label",[72,3729,223],{"class":82},[72,3731,204],{"class":82},[72,3733,3734,3737,3739,3742],{"class":74,"line":242},[72,3735,3736],{"class":219},"    text",[72,3738,223],{"class":82},[72,3740,3741],{"class":86}," labelText",[72,3743,213],{"class":82},[72,3745,3746,3749,3751,3753],{"class":74,"line":255},[72,3747,3748],{"class":219},"    showBackground",[72,3750,223],{"class":82},[72,3752,364],{"class":363},[72,3754,213],{"class":82},[72,3756,3757,3759,3761,3763,3765,3767,3769],{"class":74,"line":268},[72,3758,3704],{"class":219},[72,3760,223],{"class":82},[72,3762,169],{"class":86},[72,3764,172],{"class":82},[72,3766,3713],{"class":86},[72,3768,172],{"class":82},[72,3770,3718],{"class":86},[72,3772,3773],{"class":74,"line":279},[72,3774,868],{"class":82},[72,3776,3777,3779],{"class":74,"line":285},[72,3778,387],{"class":82},[72,3780,390],{"class":86},[18,3782,3783],{},"同时提供一个专门的相机动作：",[63,3785,3787],{"className":65,"code":3786,"language":67,"meta":68,"style":68},"function focusTestPoint(Cesium, viewer, testPoint, duration = 1.2) {\n  viewer.camera.flyTo({\n    destination: Cesium.Cartesian3.fromDegrees(testPoint.longitude, testPoint.latitude, 1800),\n    orientation: {\n      heading: Cesium.Math.toRadians(-18),\n      pitch: Cesium.Math.toRadians(-38),\n      roll: 0\n    },\n    duration\n  })\n}\n",[29,3788,3789,3822,3840,3883,3892,3922,3950,3960,3965,3970,3976],{"__ignoreMap":68},[72,3790,3791,3793,3796,3798,3800,3802,3804,3806,3808,3810,3813,3815,3818,3820],{"class":74,"line":75},[72,3792,909],{"class":137},[72,3794,3795],{"class":150}," focusTestPoint",[72,3797,457],{"class":82},[72,3799,401],{"class":787},[72,3801,201],{"class":82},[72,3803,2163],{"class":787},[72,3805,201],{"class":82},[72,3807,3596],{"class":787},[72,3809,201],{"class":82},[72,3811,3812],{"class":787}," duration",[72,3814,758],{"class":82},[72,3816,3817],{"class":236}," 1.2",[72,3819,791],{"class":82},[72,3821,204],{"class":82},[72,3823,3824,3826,3828,3831,3833,3836,3838],{"class":74,"line":106},[72,3825,3040],{"class":86},[72,3827,172],{"class":82},[72,3829,3830],{"class":86},"camera",[72,3832,172],{"class":82},[72,3834,3835],{"class":150},"flyTo",[72,3837,457],{"class":219},[72,3839,460],{"class":82},[72,3841,3842,3845,3847,3849,3851,3853,3855,3857,3859,3862,3864,3866,3868,3870,3872,3874,3876,3879,3881],{"class":74,"line":127},[72,3843,3844],{"class":219},"    destination",[72,3846,223],{"class":82},[72,3848,169],{"class":86},[72,3850,172],{"class":82},[72,3852,3579],{"class":86},[72,3854,172],{"class":82},[72,3856,3584],{"class":150},[72,3858,457],{"class":219},[72,3860,3861],{"class":86},"testPoint",[72,3863,172],{"class":82},[72,3865,1779],{"class":86},[72,3867,201],{"class":82},[72,3869,3596],{"class":86},[72,3871,172],{"class":82},[72,3873,1789],{"class":86},[72,3875,201],{"class":82},[72,3877,3878],{"class":236}," 1800",[72,3880,791],{"class":219},[72,3882,213],{"class":82},[72,3884,3885,3888,3890],{"class":74,"line":134},[72,3886,3887],{"class":219},"    orientation",[72,3889,223],{"class":82},[72,3891,204],{"class":82},[72,3893,3894,3897,3899,3901,3903,3906,3908,3911,3913,3915,3918,3920],{"class":74,"line":156},[72,3895,3896],{"class":219},"      heading",[72,3898,223],{"class":82},[72,3900,169],{"class":86},[72,3902,172],{"class":82},[72,3904,3905],{"class":86},"Math",[72,3907,172],{"class":82},[72,3909,3910],{"class":150},"toRadians",[72,3912,457],{"class":219},[72,3914,1649],{"class":82},[72,3916,3917],{"class":236},"18",[72,3919,791],{"class":219},[72,3921,213],{"class":82},[72,3923,3924,3927,3929,3931,3933,3935,3937,3939,3941,3943,3946,3948],{"class":74,"line":181},[72,3925,3926],{"class":219},"      pitch",[72,3928,223],{"class":82},[72,3930,169],{"class":86},[72,3932,172],{"class":82},[72,3934,3905],{"class":86},[72,3936,172],{"class":82},[72,3938,3910],{"class":150},[72,3940,457],{"class":219},[72,3942,1649],{"class":82},[72,3944,3945],{"class":236},"38",[72,3947,791],{"class":219},[72,3949,213],{"class":82},[72,3951,3952,3955,3957],{"class":74,"line":186},[72,3953,3954],{"class":219},"      roll",[72,3956,223],{"class":82},[72,3958,3959],{"class":236}," 0\n",[72,3961,3962],{"class":74,"line":207},[72,3963,3964],{"class":82},"    },\n",[72,3966,3967],{"class":74,"line":216},[72,3968,3969],{"class":86},"    duration\n",[72,3971,3972,3974],{"class":74,"line":228},[72,3973,1113],{"class":82},[72,3975,390],{"class":219},[72,3977,3978],{"class":74,"line":242},[72,3979,885],{"class":82},[18,3981,3982],{},"这样一来：",[22,3984,3985,3988],{},[25,3986,3987],{},"俯视时可以看热力图贴地范围",[25,3989,3990],{},"近看时可以看热力图是否爬到了 OSM Buildings 表面",[14,3992,3994],{"id":3993},"classificationtarget-在测试场景里的作用","classificationTarget 在测试场景里的作用",[18,3996,3997,3998,4000],{},"当测试环境已经把地形和 OSM Buildings 都加载进来以后，",[29,3999,619],{}," 的行为会更直观：",[22,4002,4003,4009,4015],{},[25,4004,4005,4008],{},[29,4006,4007],{},"'terrain'","：只看热力图贴地",[25,4010,4011,4014],{},[29,4012,4013],{},"'tiles'","：只看热力图贴模型",[25,4016,4017,4020],{},[29,4018,4019],{},"'both'","：同时看贴地和贴模型",[18,4022,4023,4024,4026],{},"这也是为什么这次测试场景要同时把 terrain 和 tiles 都拉起来。否则即使 SDK 层支持 ",[29,4025,4013],{},"，页面里也没有可观察的模型目标。",[14,4028,4030],{"id":4029},"classificationtarget-怎么控制地形和-3d-tiles","classificationTarget 怎么控制地形和 3D Tiles",[18,4032,4033,4034,4036],{},"旧方案需要额外把热力图纹理再绑定到 tileset，现在不需要了。显示目标统一用 ",[29,4035,619],{}," 控制：",[63,4038,4040],{"className":65,"code":4039,"language":67,"meta":68,"style":68},"function toClassificationType(Cesium, target) {\n  const normalizedTarget = normalizeClassificationTarget(target)\n\n  if (normalizedTarget === 'terrain') {\n    return Cesium.ClassificationType.TERRAIN\n  }\n\n  if (normalizedTarget === 'tiles') {\n    return Cesium.ClassificationType.CESIUM_3D_TILE\n  }\n\n  return Cesium.ClassificationType.BOTH\n}\n",[29,4041,4042,4061,4080,4084,4106,4122,4126,4130,4150,4165,4169,4173,4188],{"__ignoreMap":68},[72,4043,4044,4046,4048,4050,4052,4054,4057,4059],{"class":74,"line":75},[72,4045,909],{"class":137},[72,4047,2693],{"class":150},[72,4049,457],{"class":82},[72,4051,401],{"class":787},[72,4053,201],{"class":82},[72,4055,4056],{"class":787}," target",[72,4058,791],{"class":82},[72,4060,204],{"class":82},[72,4062,4063,4065,4068,4070,4073,4075,4078],{"class":74,"line":106},[72,4064,2457],{"class":137},[72,4066,4067],{"class":86}," normalizedTarget",[72,4069,758],{"class":82},[72,4071,4072],{"class":150}," normalizeClassificationTarget",[72,4074,457],{"class":219},[72,4076,4077],{"class":86},"target",[72,4079,390],{"class":219},[72,4081,4082],{"class":74,"line":127},[72,4083,131],{"emptyLinePlaceholder":130},[72,4085,4086,4088,4090,4093,4095,4097,4100,4102,4104],{"class":74,"line":134},[72,4087,736],{"class":78},[72,4089,739],{"class":219},[72,4091,4092],{"class":86},"normalizedTarget",[72,4094,931],{"class":82},[72,4096,96],{"class":82},[72,4098,4099],{"class":99},"terrain",[72,4101,298],{"class":82},[72,4103,748],{"class":219},[72,4105,460],{"class":82},[72,4107,4108,4110,4112,4114,4117,4119],{"class":74,"line":156},[72,4109,947],{"class":78},[72,4111,169],{"class":86},[72,4113,172],{"class":82},[72,4115,4116],{"class":86},"ClassificationType",[72,4118,172],{"class":82},[72,4120,4121],{"class":86},"TERRAIN\n",[72,4123,4124],{"class":74,"line":181},[72,4125,868],{"class":82},[72,4127,4128],{"class":74,"line":186},[72,4129,131],{"emptyLinePlaceholder":130},[72,4131,4132,4134,4136,4138,4140,4142,4144,4146,4148],{"class":74,"line":207},[72,4133,736],{"class":78},[72,4135,739],{"class":219},[72,4137,4092],{"class":86},[72,4139,931],{"class":82},[72,4141,96],{"class":82},[72,4143,515],{"class":99},[72,4145,298],{"class":82},[72,4147,748],{"class":219},[72,4149,460],{"class":82},[72,4151,4152,4154,4156,4158,4160,4162],{"class":74,"line":216},[72,4153,947],{"class":78},[72,4155,169],{"class":86},[72,4157,172],{"class":82},[72,4159,4116],{"class":86},[72,4161,172],{"class":82},[72,4163,4164],{"class":86},"CESIUM_3D_TILE\n",[72,4166,4167],{"class":74,"line":228},[72,4168,868],{"class":82},[72,4170,4171],{"class":74,"line":242},[72,4172,131],{"emptyLinePlaceholder":130},[72,4174,4175,4177,4179,4181,4183,4185],{"class":74,"line":255},[72,4176,877],{"class":78},[72,4178,169],{"class":86},[72,4180,172],{"class":82},[72,4182,4116],{"class":86},[72,4184,172],{"class":82},[72,4186,4187],{"class":86},"BOTH\n",[72,4189,4190],{"class":74,"line":268},[72,4191,885],{"class":82},[18,4193,4194],{},"这意味着：",[22,4196,4197,4202,4207],{},[25,4198,4199,4201],{},[29,4200,4007],{}," 只作用到地形",[25,4203,4204,4206],{},[29,4205,4013],{}," 只作用到 3D Tiles",[25,4208,4209,4211],{},[29,4210,4019],{}," 同时作用到两者",[18,4213,4214],{},"SDK 对外不暴露 Cesium 原生枚举，业务只需要记住三个字符串即可。",[14,4216,4217],{"id":4217},"参数更新为什么能实时生效",[18,4219,4220],{},"半径、模糊度、透明度变化时，不需要重建 Viewer，也不需要重新创建 primitive。SDK 只需要重绘热力纹理，然后把新 canvas 填回材质：",[63,4222,4224],{"className":65,"code":4223,"language":67,"meta":68,"style":68},"function syncGroundMaterial() {\n  if (!state.groundMaterial) {\n    rebuildGroundPrimitive()\n    return\n  }\n\n  state.groundMaterial.uniforms.image = state.renderResult.canvas\n}\n",[29,4225,4226,4237,4257,4264,4269,4273,4277,4309],{"__ignoreMap":68},[72,4227,4228,4230,4233,4235],{"class":74,"line":75},[72,4229,909],{"class":137},[72,4231,4232],{"class":150}," syncGroundMaterial",[72,4234,711],{"class":82},[72,4236,204],{"class":82},[72,4238,4239,4241,4243,4245,4248,4250,4253,4255],{"class":74,"line":106},[72,4240,736],{"class":78},[72,4242,739],{"class":219},[72,4244,742],{"class":82},[72,4246,4247],{"class":86},"state",[72,4249,172],{"class":82},[72,4251,4252],{"class":86},"groundMaterial",[72,4254,748],{"class":219},[72,4256,460],{"class":82},[72,4258,4259,4262],{"class":74,"line":127},[72,4260,4261],{"class":150},"    rebuildGroundPrimitive",[72,4263,153],{"class":219},[72,4265,4266],{"class":74,"line":134},[72,4267,4268],{"class":78},"    return\n",[72,4270,4271],{"class":74,"line":156},[72,4272,868],{"class":82},[72,4274,4275],{"class":74,"line":181},[72,4276,131],{"emptyLinePlaceholder":130},[72,4278,4279,4282,4284,4286,4288,4291,4293,4296,4298,4300,4302,4304,4306],{"class":74,"line":186},[72,4280,4281],{"class":86},"  state",[72,4283,172],{"class":82},[72,4285,4252],{"class":86},[72,4287,172],{"class":82},[72,4289,4290],{"class":86},"uniforms",[72,4292,172],{"class":82},[72,4294,4295],{"class":86},"image",[72,4297,758],{"class":82},[72,4299,2448],{"class":86},[72,4301,172],{"class":82},[72,4303,2477],{"class":86},[72,4305,172],{"class":82},[72,4307,4308],{"class":86},"canvas\n",[72,4310,4311],{"class":74,"line":207},[72,4312,885],{"class":82},[18,4314,4315],{},"只有两个场景需要重建 primitive：",[22,4317,4318,4323],{},[25,4319,4320,4322],{},[29,4321,408],{}," 变了",[25,4324,4325,4322],{},[29,4326,619],{},[18,4328,4329,4330,662],{},"对应的重建入口集中在 ",[29,4331,4332],{},"rebuildHeatmap()",[63,4334,4336],{"className":65,"code":4335,"language":67,"meta":68,"style":68},"function rebuildHeatmap({ rebuildPrimitive = false } = {}) {\n  if (state.destroyed) {\n    return\n  }\n\n  state.renderResult = renderHeatmapCanvas(state.parsed.points, state.bounds, state.heatmapOptions)\n\n  if (rebuildPrimitive || !state.groundPrimitive) {\n    rebuildGroundPrimitive()\n  }\n  else {\n    syncGroundMaterial()\n  }\n\n  requestSceneRender()\n}\n",[29,4337,4338,4364,4381,4385,4389,4393,4439,4443,4467,4473,4477,4484,4491,4495,4499,4506],{"__ignoreMap":68},[72,4339,4340,4342,4345,4348,4351,4353,4355,4357,4359,4362],{"class":74,"line":75},[72,4341,909],{"class":137},[72,4343,4344],{"class":150}," rebuildHeatmap",[72,4346,4347],{"class":82},"({",[72,4349,4350],{"class":787}," rebuildPrimitive",[72,4352,758],{"class":82},[72,4354,2977],{"class":363},[72,4356,90],{"class":82},[72,4358,758],{"class":82},[72,4360,4361],{"class":82}," {})",[72,4363,204],{"class":82},[72,4365,4366,4368,4370,4372,4374,4377,4379],{"class":74,"line":106},[72,4367,736],{"class":78},[72,4369,739],{"class":219},[72,4371,4247],{"class":86},[72,4373,172],{"class":82},[72,4375,4376],{"class":86},"destroyed",[72,4378,748],{"class":219},[72,4380,460],{"class":82},[72,4382,4383],{"class":74,"line":127},[72,4384,4268],{"class":78},[72,4386,4387],{"class":74,"line":134},[72,4388,868],{"class":82},[72,4390,4391],{"class":74,"line":156},[72,4392,131],{"emptyLinePlaceholder":130},[72,4394,4395,4397,4399,4401,4403,4406,4408,4410,4412,4415,4417,4420,4422,4424,4426,4428,4430,4432,4434,4437],{"class":74,"line":181},[72,4396,4281],{"class":86},[72,4398,172],{"class":82},[72,4400,2477],{"class":86},[72,4402,758],{"class":82},[72,4404,4405],{"class":150}," renderHeatmapCanvas",[72,4407,457],{"class":219},[72,4409,4247],{"class":86},[72,4411,172],{"class":82},[72,4413,4414],{"class":86},"parsed",[72,4416,172],{"class":82},[72,4418,4419],{"class":86},"points",[72,4421,201],{"class":82},[72,4423,2448],{"class":86},[72,4425,172],{"class":82},[72,4427,408],{"class":86},[72,4429,201],{"class":82},[72,4431,2448],{"class":86},[72,4433,172],{"class":82},[72,4435,4436],{"class":86},"heatmapOptions",[72,4438,390],{"class":219},[72,4440,4441],{"class":74,"line":186},[72,4442,131],{"emptyLinePlaceholder":130},[72,4444,4445,4447,4449,4452,4454,4456,4458,4460,4463,4465],{"class":74,"line":207},[72,4446,736],{"class":78},[72,4448,739],{"class":219},[72,4450,4451],{"class":86},"rebuildPrimitive",[72,4453,988],{"class":82},[72,4455,1516],{"class":82},[72,4457,4247],{"class":86},[72,4459,172],{"class":82},[72,4461,4462],{"class":86},"groundPrimitive",[72,4464,748],{"class":219},[72,4466,460],{"class":82},[72,4468,4469,4471],{"class":74,"line":216},[72,4470,4261],{"class":150},[72,4472,153],{"class":219},[72,4474,4475],{"class":74,"line":228},[72,4476,868],{"class":82},[72,4478,4479,4482],{"class":74,"line":242},[72,4480,4481],{"class":78},"  else",[72,4483,204],{"class":82},[72,4485,4486,4489],{"class":74,"line":255},[72,4487,4488],{"class":150},"    syncGroundMaterial",[72,4490,153],{"class":219},[72,4492,4493],{"class":74,"line":268},[72,4494,868],{"class":82},[72,4496,4497],{"class":74,"line":279},[72,4498,131],{"emptyLinePlaceholder":130},[72,4500,4501,4504],{"class":74,"line":285},[72,4502,4503],{"class":150},"  requestSceneRender",[72,4505,153],{"class":219},[72,4507,4508],{"class":74,"line":303},[72,4509,885],{"class":82},[18,4511,4512],{},"这就是这次改造后的关键性能边界：普通调参不重建几何，只有几何范围和分类目标变化时才重新创建 primitive。",[14,4514,4516],{"id":4515},"geojson-更新怎么处理","GeoJSON 更新怎么处理",[18,4518,4519,4520,662],{},"业务数据更新时，统一调用 ",[29,4521,4522],{},"updateGeoJson()",[63,4524,4526],{"className":65,"code":4525,"language":67,"meta":68,"style":68},"function updateGeoJson(featureCollection) {\n  state.geoJson = featureCollection || createEmptyFeatureCollection()\n  parseCurrentGeoJson()\n  rebuildHeatmap()\n\n  return getState()\n}\n",[29,4527,4528,4543,4563,4570,4577,4581,4590],{"__ignoreMap":68},[72,4529,4530,4532,4535,4537,4539,4541],{"class":74,"line":75},[72,4531,909],{"class":137},[72,4533,4534],{"class":150}," updateGeoJson",[72,4536,457],{"class":82},[72,4538,3269],{"class":787},[72,4540,791],{"class":82},[72,4542,204],{"class":82},[72,4544,4545,4547,4549,4552,4554,4556,4558,4561],{"class":74,"line":106},[72,4546,4281],{"class":86},[72,4548,172],{"class":82},[72,4550,4551],{"class":86},"geoJson",[72,4553,758],{"class":82},[72,4555,3305],{"class":86},[72,4557,988],{"class":82},[72,4559,4560],{"class":150}," createEmptyFeatureCollection",[72,4562,153],{"class":219},[72,4564,4565,4568],{"class":74,"line":127},[72,4566,4567],{"class":150},"  parseCurrentGeoJson",[72,4569,153],{"class":219},[72,4571,4572,4575],{"class":74,"line":134},[72,4573,4574],{"class":150},"  rebuildHeatmap",[72,4576,153],{"class":219},[72,4578,4579],{"class":74,"line":156},[72,4580,131],{"emptyLinePlaceholder":130},[72,4582,4583,4585,4588],{"class":74,"line":181},[72,4584,877],{"class":78},[72,4586,4587],{"class":150}," getState",[72,4589,153],{"class":219},[72,4591,4592],{"class":74,"line":186},[72,4593,885],{"class":82},[18,4595,4596],{},"它会完整执行：",[22,4598,4599,4602,4605,4608,4611],{},[25,4600,4601],{},"替换 GeoJSON",[25,4603,4604],{},"重新校验点位",[25,4606,4607],{},"重新统计 accepted\u002Fskipped\u002FminValue\u002FmaxValue",[25,4609,4610],{},"重新绘制 canvas",[25,4612,4613],{},"同步更新 ground material",[18,4615,4616],{},"这样业务侧只表达“数据变了”，热力图图层内部负责决定需要刷新哪些资源。",[18,4618,4619,4620,4623],{},"Demo 在 ",[29,4621,4622],{},"regenerate()"," 里除了更新热力图本体，还会同步刷新测试点 marker，这样重新生成随机点位之后，观察锚点也会继续跟着当前热力图数据走。",[14,4625,4626],{"id":4626},"生命周期怎么清理",[18,4628,4629,4630,662],{},"Cesium 页面最容易遗留的是 primitive 和材质状态。SDK 里把这些清理集中到 ",[29,4631,4632],{},"destroy()",[63,4634,4636],{"className":65,"code":4635,"language":67,"meta":68,"style":68},"function destroyGroundPrimitive() {\n  if (!state.groundPrimitive) {\n    return\n  }\n\n  if (viewer.scene.primitives.contains(state.groundPrimitive)) {\n    viewer.scene.primitives.remove(state.groundPrimitive)\n  }\n\n  state.groundPrimitive = null\n  state.groundAppearance = null\n  state.groundMaterial = null\n}\n\nfunction destroy() {\n  state.destroyed = true\n  destroyGroundPrimitive()\n  requestSceneRender()\n}\n",[29,4637,4638,4649,4667,4671,4675,4679,4712,4740,4744,4748,4760,4773,4785,4789,4793,4804,4816,4823,4829],{"__ignoreMap":68},[72,4639,4640,4642,4645,4647],{"class":74,"line":75},[72,4641,909],{"class":137},[72,4643,4644],{"class":150}," destroyGroundPrimitive",[72,4646,711],{"class":82},[72,4648,204],{"class":82},[72,4650,4651,4653,4655,4657,4659,4661,4663,4665],{"class":74,"line":106},[72,4652,736],{"class":78},[72,4654,739],{"class":219},[72,4656,742],{"class":82},[72,4658,4247],{"class":86},[72,4660,172],{"class":82},[72,4662,4462],{"class":86},[72,4664,748],{"class":219},[72,4666,460],{"class":82},[72,4668,4669],{"class":74,"line":127},[72,4670,4268],{"class":78},[72,4672,4673],{"class":74,"line":134},[72,4674,868],{"class":82},[72,4676,4677],{"class":74,"line":156},[72,4678,131],{"emptyLinePlaceholder":130},[72,4680,4681,4683,4685,4687,4689,4691,4693,4695,4697,4700,4702,4704,4706,4708,4710],{"class":74,"line":181},[72,4682,736],{"class":78},[72,4684,739],{"class":219},[72,4686,2191],{"class":86},[72,4688,172],{"class":82},[72,4690,2196],{"class":86},[72,4692,172],{"class":82},[72,4694,3129],{"class":86},[72,4696,172],{"class":82},[72,4698,4699],{"class":150},"contains",[72,4701,457],{"class":219},[72,4703,4247],{"class":86},[72,4705,172],{"class":82},[72,4707,4462],{"class":86},[72,4709,2199],{"class":219},[72,4711,460],{"class":82},[72,4713,4714,4717,4719,4721,4723,4725,4727,4730,4732,4734,4736,4738],{"class":74,"line":186},[72,4715,4716],{"class":86},"    viewer",[72,4718,172],{"class":82},[72,4720,2196],{"class":86},[72,4722,172],{"class":82},[72,4724,3129],{"class":86},[72,4726,172],{"class":82},[72,4728,4729],{"class":150},"remove",[72,4731,457],{"class":219},[72,4733,4247],{"class":86},[72,4735,172],{"class":82},[72,4737,4462],{"class":86},[72,4739,390],{"class":219},[72,4741,4742],{"class":74,"line":207},[72,4743,868],{"class":82},[72,4745,4746],{"class":74,"line":216},[72,4747,131],{"emptyLinePlaceholder":130},[72,4749,4750,4752,4754,4756,4758],{"class":74,"line":228},[72,4751,4281],{"class":86},[72,4753,172],{"class":82},[72,4755,4462],{"class":86},[72,4757,758],{"class":82},[72,4759,680],{"class":82},[72,4761,4762,4764,4766,4769,4771],{"class":74,"line":242},[72,4763,4281],{"class":86},[72,4765,172],{"class":82},[72,4767,4768],{"class":86},"groundAppearance",[72,4770,758],{"class":82},[72,4772,680],{"class":82},[72,4774,4775,4777,4779,4781,4783],{"class":74,"line":255},[72,4776,4281],{"class":86},[72,4778,172],{"class":82},[72,4780,4252],{"class":86},[72,4782,758],{"class":82},[72,4784,680],{"class":82},[72,4786,4787],{"class":74,"line":268},[72,4788,885],{"class":82},[72,4790,4791],{"class":74,"line":279},[72,4792,131],{"emptyLinePlaceholder":130},[72,4794,4795,4797,4800,4802],{"class":74,"line":285},[72,4796,909],{"class":137},[72,4798,4799],{"class":150}," destroy",[72,4801,711],{"class":82},[72,4803,204],{"class":82},[72,4805,4806,4808,4810,4812,4814],{"class":74,"line":303},[72,4807,4281],{"class":86},[72,4809,172],{"class":82},[72,4811,4376],{"class":86},[72,4813,758],{"class":82},[72,4815,2903],{"class":363},[72,4817,4818,4821],{"class":74,"line":316},[72,4819,4820],{"class":150},"  destroyGroundPrimitive",[72,4822,153],{"class":219},[72,4824,4825,4827],{"class":74,"line":329},[72,4826,4503],{"class":150},[72,4828,153],{"class":219},[72,4830,4831],{"class":74,"line":342},[72,4832,885],{"class":82},[18,4834,4835],{},"这样路由切换或页面销毁时，业务只需要先销毁热力图图层，再销毁 Cesium Viewer。",[18,4837,4838],{},"对于 Demo 额外加进去的资源，也会在销毁时一并清理：",[63,4840,4842],{"className":65,"code":4841,"language":67,"meta":68,"style":68},"if (sceneState.tileset && viewer.scene.primitives.contains(sceneState.tileset)) {\n  viewer.scene.primitives.remove(sceneState.tileset)\n}\n\nif (sceneState.testMarker) {\n  viewer.entities.remove(sceneState.testMarker)\n}\n",[29,4843,4844,4883,4910,4914,4918,4931,4954],{"__ignoreMap":68},[72,4845,4846,4848,4851,4853,4856,4859,4861,4863,4865,4867,4869,4871,4873,4876,4878,4881],{"class":74,"line":75},[72,4847,1503],{"class":78},[72,4849,4850],{"class":86}," (sceneState",[72,4852,172],{"class":82},[72,4854,4855],{"class":86},"tileset ",[72,4857,4858],{"class":82},"&&",[72,4860,2163],{"class":86},[72,4862,172],{"class":82},[72,4864,2196],{"class":86},[72,4866,172],{"class":82},[72,4868,3129],{"class":86},[72,4870,172],{"class":82},[72,4872,4699],{"class":150},[72,4874,4875],{"class":86},"(sceneState",[72,4877,172],{"class":82},[72,4879,4880],{"class":86},"tileset)) ",[72,4882,460],{"class":82},[72,4884,4885,4887,4889,4891,4893,4895,4897,4899,4901,4904,4906,4908],{"class":74,"line":106},[72,4886,3040],{"class":86},[72,4888,172],{"class":82},[72,4890,2196],{"class":86},[72,4892,172],{"class":82},[72,4894,3129],{"class":86},[72,4896,172],{"class":82},[72,4898,4729],{"class":150},[72,4900,457],{"class":219},[72,4902,4903],{"class":86},"sceneState",[72,4905,172],{"class":82},[72,4907,3116],{"class":86},[72,4909,390],{"class":219},[72,4911,4912],{"class":74,"line":127},[72,4913,885],{"class":82},[72,4915,4916],{"class":74,"line":134},[72,4917,131],{"emptyLinePlaceholder":130},[72,4919,4920,4922,4924,4926,4929],{"class":74,"line":156},[72,4921,1503],{"class":78},[72,4923,4850],{"class":86},[72,4925,172],{"class":82},[72,4927,4928],{"class":86},"testMarker) ",[72,4930,460],{"class":82},[72,4932,4933,4935,4937,4939,4941,4943,4945,4947,4949,4952],{"class":74,"line":181},[72,4934,3040],{"class":86},[72,4936,172],{"class":82},[72,4938,3557],{"class":86},[72,4940,172],{"class":82},[72,4942,4729],{"class":150},[72,4944,457],{"class":219},[72,4946,4903],{"class":86},[72,4948,172],{"class":82},[72,4950,4951],{"class":86},"testMarker",[72,4953,390],{"class":219},[72,4955,4956],{"class":74,"line":186},[72,4957,885],{"class":82},[14,4959,4960],{"id":4960},"当前实现边界",[18,4962,4963],{},"当前实现是二维热力纹理，不是三维体渲染。它适合表达某个地理范围内的密度、强度或风险分布。",[18,4965,4966,4967,4969,4970,4972],{},"这次用 ",[29,4968,44],{}," 统一了地形和 3D Tiles 的显示目标控制，但 Cesium 官方也明确提示：textured ",[29,4971,44],{}," 更适合“模式化覆盖”，如果业务未来要追求特别严格的地表纹理精度，仍然要结合具体场景重新评估影像方案。",[18,4974,4975],{},"当前 Demo 的 3D Tiles 测试资源使用的是 OSM Buildings，它依赖在线资源和 Ion token。如果网络或 token 不可用，热力图本体仍然能继续工作，但“贴模型”这部分就没法在页面里直接观察到。",[18,4977,4978,4979,4981],{},"当前 ",[29,4980,408],{}," 建议由业务侧显式传入。更进一步可以在 GeoJSON 解析阶段自动计算 bbox，再根据业务需要加 padding，这样热力图覆盖范围就能完全跟随数据本身。",[4983,4984,4985],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":68,"searchDepth":106,"depth":106,"links":4987},[4988,4989,4990,4991,4992,4993,4994,4995,4996,4997,4998,4999,5000,5001,5002,5003,5004],{"id":16,"depth":106,"text":16},{"id":57,"depth":106,"text":58},{"id":576,"depth":106,"text":576},{"id":632,"depth":106,"text":633},{"id":1121,"depth":106,"text":1122},{"id":1398,"depth":106,"text":1399},{"id":1607,"depth":106,"text":1608},{"id":2129,"depth":106,"text":2130},{"id":2801,"depth":106,"text":2802},{"id":3176,"depth":106,"text":3176},{"id":3537,"depth":106,"text":3537},{"id":3993,"depth":106,"text":3994},{"id":4029,"depth":106,"text":4030},{"id":4217,"depth":106,"text":4217},{"id":4515,"depth":106,"text":4516},{"id":4626,"depth":106,"text":4626},{"id":4960,"depth":106,"text":4960},"2026-04-23","记录通用 GeoJSON 热力图 SDK 的实现逻辑，包括 Cesium runtime 单例、GeoJSON 点数据解析、canvas 热力纹理生成、GroundPrimitive 分类渲染、世界地形接入和 OSM Buildings 测试场景管理。","md","\u002Fimages\u002Fdemos\u002Fcesium-geojson-heatmap.svg",{},"\u002Fblog\u002Fcesium-geojson-heatmap-sdk-implementation",{"title":5,"description":5006},"blog\u002Fcesium-geojson-heatmap-sdk-implementation","sVDTnKGLZyWyWxzLWbOz6aqqrtvAslI-8wBcVqGWMf4",[5015,5016],null,{"title":5017,"path":5018,"stem":5019,"description":5020,"children":-1},"Cesium 最高地形瓦片高度图实现说明","\u002Fblog\u002Fcesium-terrain-heightmap-implementation","blog\u002Fcesium-terrain-heightmap-implementation","记录 Cesium 高度图 Demo 的 SDK 化实现，包括矩形绘制、最高可用地形层级解析、规则网格采样、灰度高度图生成、起伏 Primitive 构建和地形深度测试。",1777372185303]