三维实体颜色单调?按高度分层染色,一眼看分布!

一、实现思路
根据实体高度进行差异化颜色渲染实现思路如下图所示:

二、示例完整代码
javascript
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>大型智慧社区 - 建筑高度差异化颜色(海量建筑)</title>
<link href="../../Build/SuperMap3D/Widgets/widgets.css" rel="stylesheet">
<link href="./css/pretty.css" rel="stylesheet">
<link href="./js/spectrum.css" rel="stylesheet" type="text/css">
<script src="./js/jquery.min.js"></script>
<script src="./js/spectrum.js"></script>
<script type="text/javascript" src="../../Build/SuperMap3D/SuperMap3D.js"></script>
<script src="./js/config.js"></script>
<style>
.tool-bar {
width: 340px;
background: rgba(30, 30, 40, 0.94);
border-radius: 10px;
padding: 14px;
color: #eee;
font-family: sans-serif;
backdrop-filter: blur(6px);
pointer-events: auto;
max-height: 90vh;
overflow-y: auto;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.param-item {
margin-bottom: 12px;
}
.param-item label {
margin-right: 8px;
font-size: 13px;
}
.color-table-preview {
background: #2c2c3a;
border-radius: 4px;
padding: 8px;
margin-top: 8px;
font-size: 12px;
}
.color-bar {
height: 26px;
width: 100%;
background: linear-gradient(to right,
#2b83ba, #abdda4, #ffffbf, #fdae61, #d7191c);
border-radius: 4px;
margin: 8px 0;
}
.color-stops {
display: flex;
justify-content: space-between;
font-size: 11px;
padding: 0 2px;
}
button {
background: #3a6ea5;
border: none;
color: white;
padding: 6px 16px;
border-radius: 5px;
cursor: pointer;
font-size: 12px;
margin-top: 8px;
transition: 0.2s;
font-weight: bold;
}
button:hover {
background: #2c5282;
transform: scale(1.02);
}
.entity-info {
font-size: 12px;
color: #ddd;
border-top: 1px solid #555;
margin-top: 10px;
padding-top: 8px;
line-height: 1.5;
}
.note {
font-size: 10px;
color: #aaa;
margin-top: 8px;
background: rgba(0, 0, 0, 0.5);
padding: 6px;
border-radius: 6px;
}
.legend {
margin-top: 8px;
font-size: 10px;
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: space-between;
}
hr {
border-color: #555;
margin: 10px 0;
}
.stat-highlight {
color: #ffaa66;
font-weight: bold;
}
.badge {
background: #2c5282;
border-radius: 12px;
padding: 2px 8px;
font-size: 10px;
display: inline-block;
}
</style>
</head>
<body>
<div id="Container"></div>
<div id='loadingbar' class="spinner">
<div class="spinner-container container1">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
<div class="spinner-container container2">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
<div class="spinner-container container3">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
</div>
<div id="toolbar" class="param-container tool-bar" style="display: none;">
<div class="param-item">
<label style="font-weight:bold; font-size:15px;">🏙️ 社区 · 建筑高度色表</label>
<div class="color-table-preview">
<div style="margin-bottom:5px;">相对高度 → 颜色映射</div>
<div class="color-bar"></div>
<div class="color-stops">
<span>0m</span><span>30m</span><span>60m</span><span>90m</span><span>120m+</span>
</div>
</div>
<div style="margin-top: 8px;">
<label>🎨 动态高度着色:</label>
<input type="checkbox" id="enableColorByHeight" checked
style="vertical-align: middle; transform: scale(1.1);">
</div>
<div class="note">
💡 <strong>大规模社区</strong> · 总建筑数量超过 <span id="buildingCountDisplay">780</span> 栋<br>
🏢 涵盖别墅区、洋房、小高层、CBD超高层、商业综合体<br>
🎯 高度从 3m 到 138m 梯度分布,实时颜色映射
</div>
<button id="applyColorTableBtn">🔄 重新应用颜色映射</button>
<hr>
<div class="entity-info" id="entityStats">
📍 正在生成海量建筑群,请稍候...
</div>
<div class="legend">
<span><span
style="display:inline-block; width:12px; height:12px; background:#2b83ba; border-radius:2px;"></span>
低层(≤15m)</span>
<span><span
style="display:inline-block; width:12px; height:12px; background:#abdda4; border-radius:2px;"></span>
中低(15-35m)</span>
<span><span
style="display:inline-block; width:12px; height:12px; background:#ffffbf; border-radius:2px;"></span>
中层(35-55m)</span>
<span><span
style="display:inline-block; width:12px; height:12px; background:#fdae61; border-radius:2px;"></span>
高层(55-85m)</span>
<span><span
style="display:inline-block; width:12px; height:12px; background:#d7191c; border-radius:2px;"></span>
超高层(85m+)</span>
</div>
<div class="note" style="margin-top: 8px;">
🧩 规划格局:环形商务核心 + 放射状街区 + 外围低密住宅区 + 绿地景观带<br>
📐 建筑数量提升至 <strong>1000+</strong>,性能优化,流畅浏览
</div>
</div>
</div>
<script>
/**
* 场景初始化
*/
function init(SuperMap3D, scene, viewer) {
viewer.resolutionScale = window.devicePixelRatio;
viewer.scene.globe.enableLighting = true;
viewer.shadows = true;
// 相机位置调整到能够俯瞰整个大型社区
viewer.scene.camera.setView({
destination: SuperMap3D.Cartesian3.fromDegrees(103.962, 30.710, 3500),
orientation: {
heading: 90,
pitch: -0.48,
roll: 0
}
});
// 生成海量社区
generateMassiveCommunity(viewer, SuperMap3D);
// 高度着色控制
let colorByHeightEnabled = true;
const enableCheckbox = document.getElementById('enableColorByHeight');
const applyBtn = document.getElementById('applyColorTableBtn');
function refreshColors() {
refreshAllBuildingColors(SuperMap3D, colorByHeightEnabled);
}
enableCheckbox.addEventListener('change', (e) => {
colorByHeightEnabled = e.target.checked;
refreshColors();
});
applyBtn.addEventListener('click', () => {
if (!colorByHeightEnabled) {
enableCheckbox.checked = true;
colorByHeightEnabled = true;
}
refreshColors();
});
// 初始应用色彩
refreshColors();
// 增加社区名称铭牌
viewer.entities.add({
position: SuperMap3D.Cartesian3.fromDegrees(104.045, 30.702, 545),
label: {
text: "🏙️ 智慧社区 · 建筑高度色谱可视化",
font: "bold 16px 'Segoe UI'",
fillColor: SuperMap3D.Color.WHITE,
backgroundColor: SuperMap3D.Color.BLACK.withAlpha(0.7),
showBackground: true,
pixelOffset: new SuperMap3D.Cartesian2(8, -40),
style: SuperMap3D.LabelStyle.FILL_AND_OUTLINE
}
});
$('#loadingbar').remove();
$('#toolbar').show();
setTimeout(() => {
updateCommunityStats();
}, 200);
}
// 全局存储生成的建筑实体
var buildingEntities = [];
/**
* 高度色表 (五级渐变,平滑过渡)
*/
function buildElevationColorTable() {
return [
{ stop: 0, color: [0.17, 0.53, 0.73, 1.0] }, // 0m 水蓝
{ stop: 30, color: [0.35, 0.71, 0.51, 1.0] }, // 30m 草绿
{ stop: 60, color: [0.84, 0.81, 0.44, 1.0] }, // 60m 金黄
{ stop: 90, color: [0.94, 0.60, 0.27, 1.0] }, // 90m 橙
{ stop: 120, color: [0.80, 0.25, 0.20, 1.0] } // 120m+ 红褐
];
}
/**
* 线性插值获取颜色
*/
function getColorFromTable(height, colorTable, SuperMap3D) {
if (!colorTable || colorTable.length === 0) return SuperMap3D.Color.WHITE;
if (height <= colorTable[0].stop) {
const c = colorTable[0].color;
return new SuperMap3D.Color(c[0], c[1], c[2], c[3]);
}
if (height >= colorTable[colorTable.length - 1].stop) {
const c = colorTable[colorTable.length - 1].color;
return new SuperMap3D.Color(c[0], c[1], c[2], c[3]);
}
for (let i = 0; i < colorTable.length - 1; i++) {
if (height >= colorTable[i].stop && height <= colorTable[i + 1].stop) {
const lower = colorTable[i];
const upper = colorTable[i + 1];
const t = (height - lower.stop) / (upper.stop - lower.stop);
const r = lower.color[0] * (1 - t) + upper.color[0] * t;
const g = lower.color[1] * (1 - t) + upper.color[1] * t;
const b = lower.color[2] * (1 - t) + upper.color[2] * t;
return new SuperMap3D.Color(r, g, b, 1.0);
}
}
const c = colorTable[0].color;
return new SuperMap3D.Color(c[0], c[1], c[2], c[3]);
}
/**
* 获取建筑高度
*/
function getBuildingHeight(entity) {
if (entity._customHeight !== undefined) return entity._customHeight;
if (entity.polygon && entity.polygon.extrudedHeight) {
const h = entity.polygon.extrudedHeight.getValue();
return typeof h === 'number' ? h : 15;
}
return 15;
}
/**
* 单个建筑材质应用
*/
function applyColorToBuilding(entity, SuperMap3D, colorTable, enableFlag) {
if (!enableFlag) {
const defaultColor = SuperMap3D.Color.LIGHTGRAY.withAlpha(0.85);
if (entity.polygon) entity.polygon.material = defaultColor;
return;
}
const height = getBuildingHeight(entity);
const color = getColorFromTable(height, colorTable, SuperMap3D);
if (entity.polygon) {
entity.polygon.material = color;
// 轮廓线根据高度微调颜色
if (entity.polygon.outline !== false) {
entity.polygon.outlineColor = height > 70 ? SuperMap3D.Color.BLACK : SuperMap3D.Color.DIM_GRAY;
}
}
}
/**
* 刷新所有建筑颜色
*/
function refreshAllBuildingColors(SuperMap3D, enable) {
if (!buildingEntities.length) return;
const colorTable = buildElevationColorTable();
for (let i = 0; i < buildingEntities.length; i++) {
const bld = buildingEntities[i];
if (bld && !bld.isDestroyed?.()) {
applyColorToBuilding(bld, SuperMap3D, colorTable, enable);
}
}
updateCommunityStats();
}
/**
* 更新统计面板
*/
function updateCommunityStats() {
if (!buildingEntities.length) {
$('#entityStats').html('🏚️ 暂无建筑数据,重新生成中...');
return;
}
const heights = buildingEntities.map(b => getBuildingHeight(b));
const minH = Math.min(...heights);
const maxH = Math.max(...heights);
const avgH = (heights.reduce((a, b) => a + b, 0) / heights.length).toFixed(1);
const lowCount = heights.filter(h => h <= 15).length;
const midLowCount = heights.filter(h => h > 15 && h <= 35).length;
const midCount = heights.filter(h => h > 35 && h <= 55).length;
const highCount = heights.filter(h => h > 55 && h <= 85).length;
const skyCount = heights.filter(h => h > 85).length;
$('#entityStats').html(`
🏙️ <strong>社区 · 实时统计</strong><br>
🧱 建筑总数: <span class="stat-highlight">${buildingEntities.length}</span> 栋<br>
📊 高度区间: ${minH}m ~ ${maxH}m | 平均 ${avgH}m<br>
🏡 低层≤15m: ${lowCount} | 🏘️ 中低15-35m: ${midLowCount}<br>
🏢 中层35-55m: ${midCount} | 🏙️ 高层55-85m: ${highCount} | 🗼 超高层85m+: ${skyCount}<br>
🎨 色表映射: 冷色调(低) → 暖色调(高)
`);
document.getElementById('buildingCountDisplay').innerText = buildingEntities.length;
}
/**
* 辅助: 创建矩形多边形坐标
*/
function createRectCoordinates(viewer, SuperMap3D, lon, lat, widthDeg, depthDeg) {
const halfW = widthDeg / 2;
const halfD = depthDeg / 2;
return SuperMap3D.Cartesian3.fromDegreesArray([
lon - halfW, lat - halfD,
lon + halfW, lat - halfD,
lon + halfW, lat + halfD,
lon - halfW, lat + halfD
]);
}
/**
* 大规模程序化社区生成 (建筑数量 1500+,布局密集但性能友好)
* 规划:市中心超高密度圈 + 多条放射轴街区 + 外围住宅区 + 别墅点缀 + 公共设施
*/
function generateMassiveCommunity(viewer, SuperMap3D) {
// 清除旧实体
for (let i = 0; i < buildingEntities.length; i++) {
const old = buildingEntities[i];
if (old && !old.isDestroyed?.()) viewer.entities.remove(old);
}
buildingEntities = [];
const centerLon = 104.062;
const centerLat = 30.672;
const baseAlt = 520;
// 工具函数快速添加建筑
function addBuilding(lon, lat, height, widthDeg, depthDeg, namePrefix = "bld") {
const positions = SuperMap3D.Cartesian3.fromDegreesArray([
lon - widthDeg / 2, lat - depthDeg / 2,
lon + widthDeg / 2, lat - depthDeg / 2,
lon + widthDeg / 2, lat + depthDeg / 2,
lon - widthDeg / 2, lat + depthDeg / 2
]);
const entity = viewer.entities.add({
name: `${namePrefix}_${lon.toFixed(5)}_${lat.toFixed(5)}`,
polygon: {
hierarchy: positions,
extrudedHeight: Math.max(2, height),
height: 0,
material: SuperMap3D.Color.WHITE,
outline: true,
outlineColor: SuperMap3D.Color.DARK_GRAY,
classificationType: undefined
}
});
entity._customHeight = Math.max(2, height);
buildingEntities.push(entity);
}
// ---------- 1. 中央CBD超级地标群 (超高层密集区) ----------
const cbdCenter = { lon: centerLon, lat: centerLat };
// 核心塔楼超高层
const megaTowers = [
{ lon: cbdCenter.lon, lat: cbdCenter.lat, height: 138, w: 0.0022, d: 0.0022 },
{ lon: cbdCenter.lon - 0.003, lat: cbdCenter.lat + 0.002, height: 112, w: 0.0024, d: 0.0024 },
{ lon: cbdCenter.lon + 0.0035, lat: cbdCenter.lat - 0.0015, height: 124, w: 0.0023, d: 0.0023 },
{ lon: cbdCenter.lon - 0.0015, lat: cbdCenter.lat - 0.003, height: 108, w: 0.0025, d: 0.0025 },
{ lon: cbdCenter.lon + 0.002, lat: cbdCenter.lat + 0.0032, height: 118, w: 0.0022, d: 0.0022 },
{ lon: cbdCenter.lon + 0.0048, lat: cbdCenter.lat + 0.001, height: 96, w: 0.0026, d: 0.0026 },
{ lon: cbdCenter.lon - 0.0042, lat: cbdCenter.lat - 0.0018, height: 104, w: 0.0024, d: 0.0024 }
];
megaTowers.forEach(t => addBuilding(t.lon, t.lat, t.height, t.w, t.d, "cbd_tower"));
// CBD内圈高强度建筑群 (高度 65~95m,密集)
for (let i = 0; i < 210; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = 0.007 + Math.random() * 0.012;
const lon = centerLon + Math.cos(angle) * radius;
const lat = centerLat + Math.sin(angle) * radius;
let height = 55 + Math.random() * 45;
const w = 0.0015 + Math.random() * 0.0012;
const d = 0.0015 + Math.random() * 0.0012;
addBuilding(lon, lat, height, w, d, "cbd_core");
}
// ---------- 2. 环形商业/办公街区 (五环放射状,增加大量中层建筑) ----------
const ringRadii = [0.014, 0.022, 0.031, 0.041];
const ringBuildingsCount = [180, 240, 310, 280];
for (let rIdx = 0; rIdx < ringRadii.length; rIdx++) {
const radius = ringRadii[rIdx];
const count = ringBuildingsCount[rIdx];
for (let i = 0; i < count; i++) {
const angle = (i / count) * Math.PI * 2 + Math.random() * 0.05;
const lon = centerLon + Math.cos(angle) * radius;
const lat = centerLat + Math.sin(angle) * radius;
let height = 0;
if (rIdx === 0) height = 48 + Math.random() * 32; // 48-80
else if (rIdx === 1) height = 35 + Math.random() * 30;
else if (rIdx === 2) height = 22 + Math.random() * 25;
else height = 14 + Math.random() * 20;
const w = 0.0014 + Math.random() * 0.001;
const d = 0.0014 + Math.random() * 0.001;
addBuilding(lon, lat, height, w, d, `ring_${rIdx}`);
}
}
// ---------- 3. 大规模住宅网格 (东南西北四大片区,每个片区超过200栋) ----------
const residentialBlocks = [
{ lonBase: centerLon - 0.048, latBase: centerLat - 0.042, cols: 28, rows: 24, startH: 8, endH: 38, stepLon: 0.0032, stepLat: 0.0030 },
{ lonBase: centerLon + 0.035, latBase: centerLat - 0.045, cols: 30, rows: 26, startH: 10, endH: 42, stepLon: 0.0030, stepLat: 0.0032 },
{ lonBase: centerLon - 0.044, latBase: centerLat + 0.038, cols: 27, rows: 25, startH: 8, endH: 35, stepLon: 0.0031, stepLat: 0.0031 },
{ lonBase: centerLon + 0.038, latBase: centerLat + 0.04, cols: 29, rows: 27, startH: 12, endH: 44, stepLon: 0.0030, stepLat: 0.0030 },
{ lonBase: centerLon - 0.012, latBase: centerLat - 0.065, cols: 32, rows: 22, startH: 14, endH: 48, stepLon: 0.0028, stepLat: 0.0029 },
{ lonBase: centerLon + 0.015, latBase: centerLat + 0.066, cols: 30, rows: 24, startH: 12, endH: 46, stepLon: 0.0029, stepLat: 0.0028 }
];
residentialBlocks.forEach(block => {
for (let row = 0; row < block.rows; row++) {
for (let col = 0; col < block.cols; col++) {
const lon = block.lonBase + col * block.stepLon;
const lat = block.latBase + row * block.stepLat;
// 排除距离中心过近的区域(避免重叠)
const distToCenter = Math.hypot(lon - centerLon, lat - centerLat);
if (distToCenter < 0.035) continue;
let heightFactor = 1.0;
if (distToCenter > 0.07) heightFactor = 0.75;
else if (distToCenter > 0.055) heightFactor = 0.88;
const height = block.startH + Math.random() * (block.endH - block.startH) * heightFactor;
const w = 0.0013 + Math.random() * 0.0009;
const d = 0.0013 + Math.random() * 0.0009;
addBuilding(lon, lat, height, w, d, "resi");
}
}
});
// ---------- 4. 低密度别墅区 & 联排 (更多建筑填充) 规划外围优美区域----------
const villaZones = [
{ centerLon: centerLon - 0.068, centerLat: centerLat - 0.058, count: 180, minH: 4, maxH: 14, radiusSpread: 0.02 },
{ centerLon: centerLon + 0.072, centerLat: centerLat - 0.055, count: 190, minH: 5, maxH: 15, radiusSpread: 0.022 },
{ centerLon: centerLon - 0.065, centerLat: centerLat + 0.062, count: 170, minH: 4, maxH: 13, radiusSpread: 0.02 },
{ centerLon: centerLon + 0.062, centerLat: centerLat + 0.064, count: 185, minH: 5, maxH: 16, radiusSpread: 0.021 },
{ centerLon: centerLon + 0.002, centerLat: centerLat - 0.082, count: 150, minH: 6, maxH: 18, radiusSpread: 0.024 },
{ centerLon: centerLon - 0.008, centerLat: centerLat + 0.086, count: 155, minH: 6, maxH: 19, radiusSpread: 0.023 }
];
villaZones.forEach(zone => {
for (let i = 0; i < zone.count; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = Math.random() * zone.radiusSpread;
const lon = zone.centerLon + Math.cos(angle) * radius;
const lat = zone.centerLat + Math.sin(angle) * radius;
const height = zone.minH + Math.random() * (zone.maxH - zone.minH);
const w = 0.0011 + Math.random() * 0.0009;
const d = 0.0011 + Math.random() * 0.0009;
addBuilding(lon, lat, height, w, d, "villa");
}
});
// ---------- 5. 商业街区和沿街店铺 (丰富街区细节,增加密度) ----------
const avenueCount = 520;
for (let i = 0; i < avenueCount; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = 0.026 + Math.random() * 0.035;
const lon = centerLon + Math.cos(angle) * radius;
const lat = centerLat + Math.sin(angle) * radius;
const height = 9 + Math.random() * 22;
const w = 0.0010 + Math.random() * 0.0007;
const d = 0.0010 + Math.random() * 0.0007;
addBuilding(lon, lat, height, w, d, "shop");
}
// ---------- 6. 公共设施 & 社区中心 (点缀高度适中) ----------
const publicCount = 180;
for (let i = 0; i < publicCount; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = 0.018 + Math.random() * 0.045;
const lon = centerLon + Math.cos(angle) * radius;
const lat = centerLat + Math.sin(angle) * radius;
const height = 7 + Math.random() * 24;
const w = 0.0015 + Math.random() * 0.001;
const d = 0.0015 + Math.random() * 0.001;
addBuilding(lon, lat, height, w, d, "public");
}
// 额外随机填充确保总体建筑超过 1800 栋(点缀)
const extraFill = 320;
for (let i = 0; i < extraFill; i++) {
const lon = centerLon + (Math.random() - 0.5) * 0.16;
const lat = centerLat + (Math.random() - 0.5) * 0.14;
const dist = Math.hypot(lon - centerLon, lat - centerLat);
if (dist < 0.01) continue;
let height = 8 + Math.random() * 45;
if (dist > 0.09) height = 6 + Math.random() * 18;
const w = 0.0012 + Math.random() * 0.001;
const d = 0.0012 + Math.random() * 0.001;
addBuilding(lon, lat, height, w, d, "extra");
}
console.log(`海量社区生成完成,总建筑实体: ${buildingEntities.length}`);
updateCommunityStats();
}
function onload(SuperMap3D) {
var EngineType = getEngineType();
var viewer = new SuperMap3D.Viewer('Container', {
contextOptions: { contextType: Number(EngineType) },
terrainProvider: new SuperMap3D.EllipsoidTerrainProvider()
});
viewer.scenePromise.then((scene) => {
init(SuperMap3D, scene, viewer);
}).catch(() => {
init(SuperMap3D, viewer.scene, viewer);
});
}
if (typeof SuperMap3D !== 'undefined') {
window.startupCalled = true;
onload(SuperMap3D);
}
</script>
</body>
</html>