ciyon_ai/aiskill/ciyon-web3d.md
2026-04-15 17:28:46 +08:00

30 KiB
Raw Blame History

Ciyon Web3D 应用开发指南

概述

本文档为AI开发3D地图应用提供指导重点讲解如何基于 cemap.html 进行二次开发,实现资产点击交互和统计图表展示等功能。

技术栈

前端

  • 核心引擎: Cesium.js (基于WebGL的3D地球可视化引擎)
  • UI框架: 自定义框架 (ciy.js, ciycmp.js)
  • 图表库: ECharts (可选,用于统计图表)

数据库

  • MySQL
  • 主要数据表:
    • zc_cemap_data - 地图配置数据
    • zc_cemap_bill - 指示牌资源
    • zc_cemap_glb - GLB模型资源

资产管理

资产说明

系统提供以下资产类型,可通过后台管理界面进行管理:

  1. GLB模型资源 - 3D建筑、设施等模型

    • 管理界面: glb.html
    • 数据表: zc_cemap_glb
    • 支持路径分类管理
  2. 指示牌资源 - 平面图片、标识牌等

    • 管理界面: bill.html
    • 数据表: zc_cemap_bill
    • 支持路径分类管理
  3. 3D Tile资源 - 实景航拍、大规模3D场景

    • 管理界面: ceeditor.html(地编器)
    • 可直接在编辑器中添加和配置

地图配置

地图配置存储在 zc_cemap_data 表的 mapjson 字段中,包含:

  • 地图源设置(天地图、高德、腾讯、百度等)
  • 3D Tile列表
  • Entity列表GLB模型、指示牌等
  • 飞行点列表
  • 地形、遮罩等设置

核心API - ciyearth4.js

1. 初始化地图

var ce = new ciyearth();
ce.init({
    domid: 'ceContainer',              // DOM容器ID或DOM对象
    personheight: 1.8,                 // 眼睛到地面高度
    timeline: 0,                       // 1显示时间轴
    ion: 'your-ion-token',             // Cesium Ion授权token
    sunhourmin: '12:12',               // 太阳时间
    setin: 3,                          // 1地球飞入, 2中国飞入, 3直接进入
    wmts_source: 1,                    // 地图源: 1默认, 2纯色, 3天地图, 4高德, 5腾讯, 6百度
    wmts_style: 1,                     // 样式: 1卫星, 2卫星(标注), 3矢量
    tiles: [],                         // 3DTile数组
    entitys: [],                       // Entity数组
    flys: []                           // 快捷定位点数组
});

3. Entity管理

Entity类型

  • model - 3D模型(GLB/GLTF)
  • billboard - 永正面(始终朝向相机)
  • plane - 墙面(平面贴图)

添加Entity

ce.addentity({
    type: 'model',                    // 类型: model/billboard/plane
    name: '建筑名称',
    bind: 'building_123',             // 资产编号,用于关联业务数据
    lng: 116.39,                      // 经度
    lat: 39.9,                        // 纬度
    height: 10,                       // 高度
    url: '/path/to/model.glb',        // 模型URL
    w: 10,                            // 宽度(米) - plane/billboard
    h: 10,                            // 高度(米) - plane/billboard
    sc: 1                             // 缩放比例
});

添加标记

ce.addmarker({
    lng: 116.39,
    lat: 39.9,
    img: '/path/to/icon.png',
    width: 20,
    height: 27,
    autoheight: true
});

4. 相机控制

飞行到指定位置

ce.flyTo({
    fd: {x, y, z},           // 目标位置
    fo: {h, p, r}            // h:heading, p:pitch, r:roll
}, 3);                       // 时间(秒)

设置相机位置

ce.setView({
    fd: {x, y, z},
    fo: {h, p, r}
});

获取相机位置

var camera = ce.getView();
// 返回: {fd: {x, y, z}, fo: {h, p, r}}

5. 事件系统

ce.ciyevent = function(type, act, data, data2) {
    if (type == 'entity') {
        if (act == 'leftclick') {
            // 左键点击Entity
            var entity = data.entity;
            var bindId = entity.ciydata.bind;  // 获取资产编号
            console.log('点击了资产:', bindId);
            
            // 在这里处理资产点击事件
            showAssetInfo(bindId);
        }
    } else if (type == 'tile') {
        if (act == 'leftclick') {
            // 左键点击3D Tile
            var primitive = data.primitive;
            console.log('点击了Tile');
        }
    } else if (type == 'control') {
        if (act == 'op') {
            // 控制操作中(飞行/行走)
            // data: Cartographic相机位置
            updateCameraInfo(data);
        }
    }
};

6. 飞行/行走模式

// 开启飞行模式
ce.control_fly_open(false);  // 飞行
ce.control_fly_open(true);   // 行走

// 关闭飞行模式
ce.control_fly_close();

键盘控制:

  • W - 前进
  • S - 后退
  • A - 左移
  • D - 右移
  • Q - 上升
  • E - 下降
  • Z - 左转
  • X - 右转
  • 空格 - 跳跃(行走模式)
  • F - 减速
  • R - 加速
  • ESC - 退出飞行模式

7. 地形管理

// 添加地形
ce.add_terrain('url', {
    url: 'https://example.com/terrain',
    requestVertexNormals: true,
    requestWaterMask: true
});

// 获取高度
var height = await ce.getposheight(cartesian3);
var floorHeight = ce.getfloorheight();

地图应用开发

基础页面结构

<!DOCTYPE html>
<html>
<head>
    <title>3D地图应用</title>
    <link href="/jscss/style.css" rel="stylesheet" type="text/css" />
    <link href="/zces108/widgets.css" rel="stylesheet" type="text/css" />
    <style type="text/css">
        #ceContainer {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
        }
        
        /* 浮动面板样式 */
        .float-panel {
            position: absolute;
            background: rgba(255, 255, 255, 0.95);
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            padding: 15px;
            z-index: 1000;
        }
        
        .float-panel.top {
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
        }
        
        .float-panel.left {
            top: 80px;
            left: 20px;
            width: 300px;
            max-height: 80vh;
            overflow-y: auto;
        }
        
        .float-panel.right {
            top: 80px;
            right: 20px;
            width: 350px;
            max-height: 80vh;
            overflow-y: auto;
        }
        
        .float-panel.bottom {
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
        }
        
        /* 控制面板 */
        .control-panel {
            position: absolute;
            right: 0;
            top: 0;
            background: rgba(255, 255, 255, 0.8);
            border-radius: 0 0 0 8px;
            padding: 10px;
        }
        
        /* 飞行点列表 */
        .fly-points {
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            display: flex;
            justify-content: center;
            flex-wrap: wrap;
            gap: 10px;
            padding-bottom: 30px;
        }
        
        .fly-points > div {
            padding: 8px 20px;
            background: rgba(255, 255, 255, 0.9);
            border: 1px solid #ddd;
            border-radius: 20px;
            color: #333;
            cursor: pointer;
            transition: all 0.3s;
        }
        
        .fly-points > div:hover {
            background: #fff;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        
        .fly-points > div.select {
            background: #007aff;
            color: #fff;
            border-color: #007aff;
        }
        
        /* 图表容器 */
        .chart-container {
            width: 100%;
            height: 200px;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <div id="ceContainer"></div>
    
    <!-- 控制面板 -->
    <div class="control-panel">
        <div class="flex">
            <div class="tip flex1" id="cameraTip"></div>
            <div class="btn xs def" onclick="ce.control_fly_open(false)">飞行</div>
            <div class="btn xs def" onclick="ce.control_fly_open(true)">步行</div>
        </div>
    </div>
    
    <!-- 飞行点列表 -->
    <div class="fly-points" id="flyPoints"></div>
    
    <!-- 左侧资产列表 -->
    <div class="float-panel left" id="assetPanel" style="display:none;">
        <h3>资产信息</h3>
        <div id="assetInfo"></div>
        <div id="assetCharts"></div>
    </div>
    
    <!-- 右侧统计面板 -->
    <div class="float-panel right" id="statPanel">
        <h3>统计数据</h3>
        <div id="statContent"></div>
    </div>
    
    <!-- 加载脚本 -->
    <script type="text/javascript" src="/jscss/ciy.js"></script>
    <script type="text/javascript" src="/zces108/Cesium.js"></script>
    <script type="text/javascript" src="/zces108/ciyearth.min.js"></script>
    <script type="text/javascript" src="/zces108/map.min.js"></script>
    <script type="text/javascript" src="/jscss/echarts.min.js"></script>
    
    <script type="text/javascript">
        'use strict';
        var ce = new ciyearth();
        var currentAssetId = null;
        var statChart = null;
        
        // 初始化地图
        function initMap() {
            ciyfn.callfunc('init', {}, function (json) {
                var mapset = ciyfn.tojson(json.cemap.mapjson);
                mapset.domid = 'ceContainer';
                ce.init(mapset);
                
                // 渲染飞行点
                renderFlyPoints(mapset.flys);
                
                // 初始化统计图表
                initStatChart();
            });
        }
        
        // 渲染飞行点
        function renderFlyPoints(flys) {
            if (!flys || flys.length === 0) return;
            
            var container = $5('#flyPoints');
            for (var i = 0; i < flys.length; i++) {
                var html = $5('<div onclick="flyTo(this)">' + flys[i].name + '</div>');
                html.attr('data-dat', flys[i].dat);
                container.append(html);
            }
        }
        
        // 飞行到指定点
        function flyTo(dom) {
            event.stopPropagation();
            $5('.fly-points>div').removeClass('select');
            $5(dom).addClass('select');
            var dat = $5(dom).attr('data-dat');
            ce.flyTo(ciyfn.tojson(dat), 3);
        }
        
        // 显示资产信息
        function showAssetInfo(assetId) {
            currentAssetId = assetId;
            
            // 显示左侧面板
            $5('#assetPanel').show();
            
            // 获取资产详情
            ciyfn.callfunc('getAssetDetail', {id: assetId}, function (json) {
                var html = '<div class="asset-detail">';
                html += '<p><strong>名称:</strong> ' + json.data.name + '</p>';
                html += '<p><strong>类型:</strong> ' + json.data.type + '</p>';
                html += '<p><strong>位置:</strong> ' + json.data.location + '</p>';
                html += '<p><strong>状态:</strong> ' + json.data.status + '</p>';
                html += '</div>';
                
                $5('#assetInfo').html(html);
                
                // 渲染资产相关图表
                renderAssetCharts(json.data);
            });
        }
        
        // 渲染资产图表
        function renderAssetCharts(assetData) {
            var chartsContainer = $5('#assetCharts');
            chartsContainer.html('');
            
            // 示例:渲染趋势图
            var chart1 = $5('<div class="chart-container" id="chart1"></div>');
            chartsContainer.append(chart1);
            
            var option1 = {
                title: { text: '近7天访问量', left: 'center', textStyle: {fontSize: 12} },
                xAxis: { type: 'category', data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] },
                yAxis: { type: 'value' },
                series: [{
                    data: assetData.visits || [120, 200, 150, 80, 70, 110, 130],
                    type: 'line',
                    smooth: true,
                    areaStyle: { opacity: 0.3 }
                }]
            };
            
            var chart1Instance = echarts.init(document.getElementById('chart1'));
            chart1Instance.setOption(option1);
            
            // 示例:渲染分布图
            var chart2 = $5('<div class="chart-container" id="chart2"></div>');
            chartsContainer.append(chart2);
            
            var option2 = {
                title: { text: '时段分布', left: 'center', textStyle: {fontSize: 12} },
                xAxis: { type: 'category', data: ['00-06', '06-12', '12-18', '18-24'] },
                yAxis: { type: 'value' },
                series: [{
                    data: assetData.timeDist || [20, 150, 280, 90],
                    type: 'bar',
                    itemStyle: { color: '#5470c6' }
                }]
            };
            
            var chart2Instance = echarts.init(document.getElementById('chart2'));
            chart2Instance.setOption(option2);
            
            // 监听窗口大小变化
            window.addEventListener('resize', function() {
                chart1Instance.resize();
                chart2Instance.resize();
            });
        }
        
        // 初始化统计图表
        function initStatChart() {
            var container = $5('#statContent');
            container.html('<div class="chart-container" id="statChart"></div>');
            
            statChart = echarts.init(document.getElementById('statChart'));
            
            var option = {
                title: { text: '区域统计', left: 'center', textStyle: {fontSize: 14} },
                tooltip: { trigger: 'axis' },
                legend: { bottom: 0 },
                xAxis: { type: 'category', data: ['区域A', '区域B', '区域C', '区域D'] },
                yAxis: { type: 'value' },
                series: [
                    { name: '建筑数', type: 'bar', data: [120, 200, 150, 80] },
                    { name: '设备数', type: 'bar', data: [220, 182, 191, 234] }
                ]
            };
            
            statChart.setOption(option);
            
            window.addEventListener('resize', function() {
                statChart.resize();
            });
        }
        
        // 更新相机信息
        function updateCameraInfo(cartographic) {
            var lng = Cesium.Math.toDegrees(cartographic.longitude);
            var lat = Cesium.Math.toDegrees(cartographic.latitude);
            var html = '经度:' + lng.toFixed(4) + ' 纬度:' + lat.toFixed(4) + ' 海拔:' + cartographic.height.toFixed(1) + '米';
            $5('#cameraTip').html(html);
        }
        
        // 设置事件回调
        ce.ciyevent = function (type, act, data, data2) {
            if (type == 'entity') {
                if (act == 'leftclick') {
                    // 点击Entity
                    var entity = data.entity;
                    var bindId = entity.ciydata.bind;
                    if (bindId) {
                        showAssetInfo(bindId);
                    }
                }
            } else if (type == 'control') {
                if (act == 'op') {
                    // 控制操作中
                    updateCameraInfo(data);
                }
            }
        };
        
        // 页面加载完成后初始化
        ciyfn.pageload(function () {
            try {
                initMap();
            } catch (e) {
                console.log('init:', e);
            }
        });
    </script>
</body>
</html>

事件交互

Entity点击事件

ce.ciyevent = function(type, act, data, data2) {
    if (type == 'entity' && act == 'leftclick') {
        var entity = data.entity;
        
        // 获取Entity信息
        var entityInfo = {
            id: entity.id,
            name: entity.name,
            type: entity.ciytype,
            bind: entity.ciydata.bind,  // 资产编号
            position: entity.position._value,
            customData: entity.ciydata  // 自定义数据
        };
        
        console.log('点击Entity:', entityInfo);
        
        // 处理点击事件
        handleEntityClick(entityInfo);
    }
};

Tile点击事件

if (type == 'tile' && act == 'leftclick') {
    var primitive = data.primitive;
    console.log('点击3D Tile:', primitive);
    
    // 可以通过拾取获取Tile中的具体建筑信息
    // 这需要3D Tile数据中包含属性信息
}

自定义点击事件

// 添加自定义点击处理器
var handler = new Cesium.ScreenSpaceEventHandler(ce.viewer.canvas);
handler.setInputAction(function(click) {
    var pickedObject = ce.viewer.scene.pick(click.position);
    
    if (Cesium.defined(pickedObject)) {
        if (pickedObject.id) {
            // 点击了Entity
            var entity = pickedObject.id;
            console.log('Entity:', entity.name, entity.ciydata);
        } else if (pickedObject.primitive) {
            // 点击了Primitive如3D Tile
            console.log('Primitive:', pickedObject.primitive);
        }
    } else {
        // 点击了空白处
        console.log('点击空白处');
        // 可以关闭浮动面板
        $5('#assetPanel').hide();
    }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

浮动面板

创建浮动面板

// 左侧面板 - 资产信息
var leftPanel = document.createElement('div');
leftPanel.className = 'float-panel left';
leftPanel.style.cssText = 'top: 80px; left: 20px; width: 320px;';
leftPanel.innerHTML = '<h3>资产信息</h3><div id="assetContent"></div>';
document.body.appendChild(leftPanel);

// 右侧面板 - 统计图表
var rightPanel = document.createElement('div');
rightPanel.className = 'float-panel right';
rightPanel.style.cssText = 'top: 80px; right: 20px; width: 380px;';
rightPanel.innerHTML = '<h3>统计数据</h3><div id="statContent"></div>';
document.body.appendChild(rightPanel);

// 顶部面板 - 搜索/筛选
var topPanel = document.createElement('div');
topPanel.className = 'float-panel top';
topPanel.innerHTML = '<input type="text" placeholder="搜索资产..." />';
document.body.appendChild(topPanel);

// 底部面板 - 快捷操作
var bottomPanel = document.createElement('div');
bottomPanel.className = 'float-panel bottom';
bottomPanel.innerHTML = '<button onclick="resetView()">重置视角</button>';
document.body.appendChild(bottomPanel);

面板样式

.float-panel {
    position: absolute;
    background: rgba(255, 255, 255, 0.95);
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    padding: 15px;
    z-index: 1000;
    transition: all 0.3s ease;
}

.float-panel.top {
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
}

.float-panel.left {
    top: 80px;
    left: 20px;
    width: 320px;
    max-height: calc(100vh - 100px);
    overflow-y: auto;
}

.float-panel.right {
    top: 80px;
    right: 20px;
    width: 380px;
    max-height: calc(100vh - 100px);
    overflow-y: auto;
}

.float-panel.bottom {
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
}

/* 面板折叠状态 */
.float-panel.collapsed {
    transform: scale(0);
    opacity: 0;
    pointer-events: none;
}

面板显示/隐藏

// 显示面板
function showPanel(selector) {
    $5(selector).removeClass('collapsed');
}

// 隐藏面板
function hidePanel(selector) {
    $5(selector).addClass('collapsed');
}

// 切换面板
function togglePanel(selector) {
    $5(selector).toggleClass('collapsed');
}

统计图表

使用ECharts渲染图表

// 渲染柱状图
function renderBarChart(containerId, data) {
    var chart = echarts.init(document.getElementById(containerId));
    
    var option = {
        title: { text: data.title || '柱状图', left: 'center', textStyle: {fontSize: 12} },
        tooltip: { trigger: 'axis' },
        xAxis: { type: 'category', data: data.categories || [] },
        yAxis: { type: 'value' },
        series: [{
            name: data.seriesName || '数值',
            type: 'bar',
            data: data.values || [],
            itemStyle: { color: '#5470c6' }
        }]
    };
    
    chart.setOption(option);
    return chart;
}

// 渲染折线图
function renderLineChart(containerId, data) {
    var chart = echarts.init(document.getElementById(containerId));
    
    var option = {
        title: { text: data.title || '折线图', left: 'center', textStyle: {fontSize: 12} },
        tooltip: { trigger: 'axis' },
        xAxis: { type: 'category', data: data.categories || [] },
        yAxis: { type: 'value' },
        series: [{
            name: data.seriesName || '数值',
            type: 'line',
            data: data.values || [],
            smooth: true,
            areaStyle: { opacity: 0.3 }
        }]
    };
    
    chart.setOption(option);
    return chart;
}

// 渲染饼图
function renderPieChart(containerId, data) {
    var chart = echarts.init(document.getElementById(containerId));
    
    var option = {
        title: { text: data.title || '饼图', left: 'center', textStyle: {fontSize: 12} },
        tooltip: { trigger: 'item' },
        legend: { bottom: 0 },
        series: [{
            type: 'pie',
            radius: '50%',
            data: data.values || [],
            emphasis: {
                itemStyle: {
                    shadowBlur: 10,
                    shadowOffsetX: 0,
                    shadowColor: 'rgba(0, 0, 0, 0.5)'
                }
            }
        }]
    };
    
    chart.setOption(option);
    return chart;
}

响应式图表

// 监听窗口大小变化
var charts = [];

function addChart(chart) {
    charts.push(chart);
}

window.addEventListener('resize', function() {
    charts.forEach(function(chart) {
        chart.resize();
    });
});

完整示例

示例:智慧园区管理

// 资产数据映射
var assetDataMap = {
    'building_001': {
        name: 'A栋办公楼',
        type: '办公楼',
        floors: 18,
        area: 12000,
        occupancy: 85,
        status: '正常',
        energy: { today: 4500, month: 120000 },
        visits: [120, 200, 150, 80, 70, 110, 130],
        timeDist: [20, 150, 280, 90]
    },
    'building_002': {
        name: 'B栋研发中心',
        type: '研发中心',
        floors: 12,
        area: 8000,
        occupancy: 92,
        status: '正常',
        energy: { today: 3800, month: 98000 },
        visits: [90, 150, 180, 100, 60, 130, 140],
        timeDist: [15, 120, 250, 80]
    }
};

// 处理Entity点击
function handleEntityClick(entityInfo) {
    var assetId = entityInfo.bind;
    
    if (!assetId || !assetDataMap[assetId]) {
        console.log('未知资产:', assetId);
        return;
    }
    
    var asset = assetDataMap[assetId];
    
    // 显示资产信息面板
    showAssetPanel(asset);
    
    // 更新统计图表
    updateStatCharts(asset);
    
    // 飞行到资产位置
    if (entityInfo.position) {
        ce.viewer.flyTo(ce.viewer.entities.getById(entityInfo.id), {
            offset: new Cesium.HeadingPitchRange(1.57, -0.6, 50)
        });
    }
}

// 显示资产信息面板
function showAssetPanel(asset) {
    var html = '<div class="asset-info">';
    html += '<h4>' + asset.name + '</h4>';
    html += '<div class="info-grid">';
    html += '<div><span>类型:</span>' + asset.type + '</div>';
    html += '<div><span>楼层:</span>' + asset.floors + '层</div>';
    html += '<div><span>面积:</span>' + asset.area + '㎡</div>';
    html += '<div><span>入住率:</span>' + asset.occupancy + '%</div>';
    html += '<div><span>状态:</span><span class="status ' + asset.status + '">' + asset.status + '</span></div>';
    html += '</div>';
    html += '<div class="energy-info">';
    html += '<div><span>今日能耗:</span>' + asset.energy.today + ' kWh</div>';
    html += '<div><span>本月能耗:</span>' + asset.energy.month + ' kWh</div>';
    html += '</div>';
    html += '</div>';
    
    $5('#assetContent').html(html);
}

// 更新统计图表
function updateStatCharts(asset) {
    // 更新访问量趋势图
    var visitsChart = echarts.init(document.getElementById('visitsChart'));
    visitsChart.setOption({
        series: [{ data: asset.visits }]
    });
    
    // 更新时段分布图
    var timeDistChart = echarts.init(document.getElementById('timeDistChart'));
    timeDistChart.setOption({
        series: [{ data: asset.timeDist }]
    });
}

// 初始化页面
function initPage() {
    // 初始化地图
    ce.init({
        domid: 'ceContainer',
        wmts_source: 4,
        wmts_style: 3,
        setin: 3,
        entitys: [
            {
                type: 'model',
                name: 'A栋办公楼',
                bind: 'building_001',
                lng: 116.39,
                lat: 39.9,
                height: 0,
                url: '/models/building_a.glb'
            },
            {
                type: 'model',
                name: 'B栋研发中心',
                bind: 'building_002',
                lng: 116.40,
                lat: 39.9,
                height: 0,
                url: '/models/building_b.glb'
            }
        ]
    });
    
    // 设置事件回调
    ce.ciyevent = function(type, act, data) {
        if (type == 'entity' && act == 'leftclick') {
            var entity = data.entity;
            handleEntityClick({
                id: entity.id,
                bind: entity.ciydata.bind,
                position: entity.position._value
            });
        }
    };
}

// 页面加载完成后执行
ciyfn.pageload(initPage);

最佳实践

1. 性能优化

// 使用requestRenderMode按需渲染
ce.init({
    domid: 'ceContainer',
    requestRenderMode: true,
    maximumRenderTimeChange: Infinity
});

// 距离优化只加载视野内的Entity
ce.reloadentity();  // 自动优化加载

// 减少Entity数量
// 对于大量相似Entity考虑使用3D Tile或PrimitiveCollection

2. 事件处理

// 使用节流防止频繁触发
function throttle(func, delay) {
    var lastTime = 0;
    return function() {
        var now = Date.now();
        if (now - lastTime >= delay) {
            lastTime = now;
            func.apply(this, arguments);
        }
    };
}

// 应用节流
ce.ciyevent = throttle(function(type, act, data) {
    // 处理事件
}, 100);

3. 内存管理

// 销毁图表实例
function disposeCharts() {
    charts.forEach(function(chart) {
        chart.dispose();
    });
    charts = [];
}

// 页面卸载时清理
window.addEventListener('beforeunload', function() {
    disposeCharts();
    ce.destroy();
});

4. 响应式设计

// 监听窗口大小变化
window.addEventListener('resize', function() {
    // 调整图表大小
    charts.forEach(function(chart) {
        chart.resize();
    });
    
    // 调整面板位置
    updatePanelPositions();
});

// 移动端适配
if (ciyfn.inmobile()) {
    // 隐藏部分面板
    $5('.control-panel').hide();
    // 调整面板样式
    $5('.float-panel.left').css('width', '80%');
}

常见问题

1. 如何获取点击的Entity详细信息

ce.ciyevent = function(type, act, data) {
    if (type == 'entity' && act == 'leftclick') {
        var entity = data.entity;
        console.log('Entity ID:', entity.id);
        console.log('Entity Name:', entity.name);
        console.log('Entity Type:', entity.ciytype);
        console.log('Custom Data:', entity.ciydata);
        console.log('Position:', entity.position._value);
    }
};

2. 如何在点击时高亮显示Entity

// 使用轮廓高亮
ce.silhouette.selected = [entity];

// 使用颜色高亮
entity.model.color = Cesium.Color.fromCssColorString('#ffff00');

// 重置高亮
ce.silhouette.selected = [];
entity.model.color = Cesium.Color.WHITE;

3. 如何加载大量Entity而不影响性能

// 分批加载
var batchSize = 100;
var currentIndex = 0;

function loadBatch() {
    var batch = entities.slice(currentIndex, currentIndex + batchSize);
    batch.forEach(function(entityData) {
        ce.addentity(entityData);
    });
    currentIndex += batchSize;
    
    if (currentIndex < entities.length) {
        setTimeout(loadBatch, 100);
    }
}

loadBatch();

4. 如何实现图层的显示/隐藏?

// 给Entity添加分组属性
ce.addentity({
    type: 'model',
    name: '建筑1',
    category: 'building',  // 分类
    // ...
});

// 控制图层显示
function toggleLayer(category, visible) {
    for (var id in ce.entitys) {
        var entity = ce.entitys[id];
        if (entity.ciydata.category === category) {
            entity.show = visible;
        }
    }
}

// 使用
toggleLayer('building', false);  // 隐藏建筑层
toggleLayer('building', true);   // 显示建筑层

5. 如何实现搜索定位功能?

// 搜索Entity
function searchEntity(keyword) {
    for (var id in ce.entitys) {
        var entity = ce.entitys[id];
        if (entity.name && entity.name.indexOf(keyword) > -1) {
            // 找到匹配的Entity
            flyToEntity(id);
            return;
        }
    }
}

// 飞行到Entity
function flyToEntity(entityId) {
    var entity = ce.entitys[entityId];
    ce.viewer.flyTo(entity, {
        offset: new Cesium.HeadingPitchRange(1.57, -0.6, 50)
    });
    
    // 高亮显示
    ce.silhouette.selected = [entity];
}

参考资料