learning_cesium/workshop.html

449 lines
16 KiB
HTML
Raw Permalink Normal View History

2024-03-19 18:06:25 +08:00
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Use correct character set. -->
<meta charset="utf-8">
<!-- Tell IE to use the latest, best version. -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>Learning Cesium!</title>
<script src="./Build/Cesium/Cesium.js"></script>
<style>
@import url(./Build/Cesium/Widgets/widgets.css);
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
}
.backdrop {
display: inline-block;
background: rgba(42, 42, 42, 0.7);
border-radius: 5px;
border: 1px solid #444;
padding: 5px 10px;
color: #fff;
line-height: 150%;
font-size: small;
}
#heightSliderLabel, #heightValue {
vertical-align: top;
}
.backdrop a:link, .backdrop a:visited, .backdrop a:hover {
color: #fff
}
.loadingIndicator {
display: none;
position: absolute;
top: 50%;
left: 50%;
margin-top: -33px;
margin-left: -33px;
width: 66px;
height: 66px;
background: url(Source/Images/ajax-loader.gif) center no-repeat;
}
.cover {
display: none;
position: absolute;
background-color: rgba(0, 0, 0, 0.75);
top: 0;
left: 0;
right: 0;
bottom: 0;
}
#menu {
position: absolute;
left: 10px;
top: 10px;
}
.nowrap {
white-space: nowrap;
}
html, body, #cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
font-family: sans-serif;
background: #000;
}
button.cesium-infoBox-camera {
display: none;
}
#3DTiles {
padding-top: 10px;
}
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<!-- Some input elements for our app. -->
<div class="backdrop" id="menu">
<h2>Sample NYC Geocaches</h2>
<span><strong>Camera Mode</strong></span>
<div class="nowrap">
<input id="freeMode" name="source" type="radio" checked/>
<label for="freeMode">Free</label>
</div>
<div class="nowrap">
<input id="droneMode" name="source" type="radio"/>
<label for="droneMode">Drone View</label>
</div>
<br>
<span><strong>3d Tile Styling</strong></span>
<div class="nowrap">
<select id="tileStyle">
<option value="none">None</option>
<option value="height">Height</option>
<option value="transparent">Transparent</option>
</select>
</div>
<br>
<span><strong>Display Options</strong></span>
<div class="nowrap">
<input id="shadows" type="checkbox"/>
<label for="shadows">Shadows</label>
</div>
<div class="nowrap">
<input id="neighborhoods" type="checkbox" checked/>
<label for="neighborhoods">Neighborhoods</label>
</div>
<br>
</div>
<div id="loadingIndicator" class="cover">
<div id="loadingIcon" class="loadingIndicator"></div>
</div>
<script>
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5ZjRjNTZkNC01NDYxLTRhMjQtOGEwZC1kZjA3YzQ5YTJlZDkiLCJpZCI6MjYwODQsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODcxOTMwODN9.prGsSKyAW_9Ow5zHYPhbm3LsQL-ApQw5-5PNJkDaHi8';
var viewer = new Cesium.Viewer('cesiumContainer',{
scene3DOnly: true,
selectionIndicator: false,
baseLayerPicker: false
});
// 移除默认的底图
viewer.imageryLayers.remove(viewer.imageryLayers.get(0));
// 添加哨兵影像 Sentinel-2 imagery
viewer.imageryLayers.addImageryProvider(new Cesium.IonImageryProvider({ assetId : 3954 }));
viewer.terrainProvider = Cesium.createWorldTerrain({
requestWaterMask : true, // required for water effects
requestVertexNormals : true // required for terrain lighting
});
// 确保地形后面的物体能够被正确遮挡
viewer.scene.globe.depthTestAgainstTerrain = true;
// 使场景中的灯光随着时间的变化而变化。如果你缩小,你会看到地球的那部分是黑暗的,因为太阳已经在那部分落下了。
viewer.scene.globe.enableLighting = true;
// Create an initial camera view
var initialPosition = new Cesium.Cartesian3.fromDegrees(-73.998114468289017509, 40.674512895646692812, 2631.082799425431);
var initialOrientation = new Cesium.HeadingPitchRoll.fromDegrees(7.1077496389876024807, -31.987223091598949054, 0.025883251314954971306);
var homeCameraView = {
destination : initialPosition,
orientation : {
heading : initialOrientation.heading,
pitch : initialOrientation.pitch,
roll : initialOrientation.roll
}
};
// Set the initial view
viewer.scene.camera.setView(homeCameraView);
// Add some camera flight animation options
homeCameraView.duration = 2.0;
homeCameraView.maximumHeight = 2000;
homeCameraView.pitchAdjustHeight = 2000;
homeCameraView.endTransform = Cesium.Matrix4.IDENTITY;
// 覆盖 homeButton 的点击事件
viewer.homeButton.viewModel.command.beforeExecute.addEventListener(function (e) {
e.cancel = true;
viewer.scene.camera.flyTo(homeCameraView);
});
// Set up clock and timeline.
viewer.clock.shouldAnimate = true; // make the animation play when the viewer starts
viewer.clock.startTime = Cesium.JulianDate.fromIso8601("2017-07-11T16:00:00Z");
viewer.clock.stopTime = Cesium.JulianDate.fromIso8601("2017-07-11T16:20:00Z");
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2017-07-11T16:00:00Z");
viewer.clock.multiplier = 2; // sets a speedup
viewer.clock.clockStep = Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER; // tick computation mode
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // loop at the end
viewer.timeline.zoomTo(viewer.clock.startTime, viewer.clock.stopTime); // set visible range
/*
Cesium 中对KML的支持还不是很完善但实现了大部分标准和Google的gx扩展命名空间。
Cesium 提供KmlDataSource 类来解析 KML和KMZ数据
配置项中 camera 和 canvas 是必需的;
KmlDataSource 实例的 load 方法是异步的,返回一个 Promise 实例对象,
可在请求完成后的 then 方法后处理加载的数据源
*/
var kmlOptions = {
camera : viewer.scene.camera,
canvas : viewer.scene.canvas,
clampToGround : true//使得实体贴合地形,而不是弯曲的WGS84椭球面
};
var geocachePromise = Cesium.KmlDataSource.load('SampleData/sampleGeocacheLocations.kml', kmlOptions);
// Add geocache billboard entities to scene and style them
geocachePromise.then(function(dataSource) {
// 将数据源中的数据作为实体添加到 viewer 实例中
viewer.dataSources.add(dataSource);
// 获取实体数组
var geocacheEntities = dataSource.entities.values;
for (var i = 0; i < geocacheEntities.length; i++) {
var entity = geocacheEntities[i];
if (Cesium.defined(entity.billboard)) {
// Entity styling code here
// 调整实体垂直方向上的位置,使其于地形贴合,不至于被地形遮挡
entity.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
// 取消显示标注
entity.label = undefined;
// 添加可见范围
entity.billboard.distanceDisplayCondition = new Cesium.DistanceDisplayCondition(10.0, 20000.0);
// 转换坐标,将实体的位置坐标 Cartesian3 转成经纬度 Cartographic ,用于显示
var cartographicPosition = Cesium.Cartographic.fromCartesian(entity.position.getValue(Cesium.JulianDate.now()));
// 将弧度制转成度
var longitude = Cesium.Math.toDegrees(cartographicPosition.longitude);
var latitude = Cesium.Math.toDegrees(cartographicPosition.latitude);
// 修改实体描述
var description = '<table class="cesium-infoBox-defaultTable cesium-infoBox-defaultTable-lighter"><tbody>' +
'<tr><th>' + "Longitude" + '</th><td>' + longitude.toFixed(5) + '</td></tr>' +
'<tr><th>' + "Latitude" + '</th><td>' + latitude.toFixed(5) + '</td></tr>' +
'<tr><th>' + "JulianDate" + '</th><td>' + Cesium.JulianDate.now() + '</td></tr>' +
// '<tr><th>' + "entity.position" + '</th><td>' + entity.position.getValue(Cesium.JulianDate.now()) + '</td></tr>' +
// '<tr><th>' + "fromDate" + '</th><td>' + entity.position.getValue(Cesium.JulianDate.fromDate(new Date("2019-12-12"))) + '</td></tr>' +
// '<tr><th>' + "cartographicPosition.latitude" + '</th><td>' + cartographicPosition.latitude + '</td></tr>' +
'</tbody></table>';
entity.description = description;
}
}
});
/**
* 加载 GeoJson 数据
* 使用 GeoJsonDataSource 类解析 GeoJson 数据
*/
var geojsonOptions = {
clampToGround : true //贴合地形
};
// 加载geojson格式的社区数据
var neighborhoodsPromise = Cesium.GeoJsonDataSource.load('SampleData/sampleNeighborhoods.geojson', geojsonOptions);
// 用于保存添加的实体集合
var neighborhoods;
neighborhoodsPromise.then(function(dataSource) {
// 将请求获得的数据添加到 viewer 中
viewer.dataSources.add(dataSource);
neighborhoods = dataSource.entities;
// 获取实体数组
var neighborhoodEntities = dataSource.entities.values;
for (var i = 0; i < neighborhoodEntities.length; i++) {
var entity = neighborhoodEntities[i];
if (Cesium.defined(entity.polygon)) {
// Cesium 将 GeoJson 的属性数据存储在实体的properties 属性中
entity.name = entity.properties.neighborhood;
// 使用随机颜色样式作为填充材料.
entity.polygon.material = Cesium.Color.fromRandom({
red : 0.1,
maximumGreen : 0.5,
minimumBlue : 0.5,
alpha : 0.6
});
// Tells the polygon to color the terrain.
//ClassificationType.CESIUM_3D_TILE will color the 3D tileset, and ClassificationType.BOTH will color both the 3d tiles and terrain (BOTH is the default)
entity.polygon.classificationType = Cesium.ClassificationType.TERRAIN;
// 获取面的位置坐标信息
var polyPositions = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
var polyCenter = Cesium.BoundingSphere.fromPoints(polyPositions).center; //获取面的中心点
polyCenter = Cesium.Ellipsoid.WGS84.scaleToGeodeticSurface(polyCenter);
entity.position = polyCenter;
// Generate labels
entity.label = {
text : entity.name,
showBackground : true,//指定标签背景的可见性。
scale : 0.6,//原图像的比例
horizontalOrigin : Cesium.HorizontalOrigin.CENTER,//水平对齐方式
verticalOrigin : Cesium.VerticalOrigin.BOTTOM,//垂直对齐方式
distanceDisplayCondition : new Cesium.DistanceDisplayCondition(10.0, 8000.0),//设置于 Camera 距离的显示范围
disableDepthTestDistance : 100.0
};
}
}
});
/**
* 加载飞机模型
* 1、CZML数据将时间动态图形加载进来设置 clock 将时间于时态数据对齐
* 2、获取数据源中的轨迹移动点绑定模型
*/
// Load a drone flight path from a CZML file
var dronePromise = Cesium.CzmlDataSource.load('SampleData/sampleFlight.czml');
var drone;
dronePromise.then(function(dataSource) {
viewer.dataSources.add(dataSource);
// Get the entity using the id defined in the CZML data
drone = dataSource.entities.getById('Aircraft/Aircraft1');
// 挂载 3维模型
drone.model = {
uri : 'SampleData/Models/CesiumDrone.gltf',
minimumPixelSize : 128,
maximumScale : 1000,
silhouetteColor : Cesium.Color.WHITE,
silhouetteSize : 2
};
// VelocityOrientationProperty 会在时间上向前和向后采样的实体的位置,自动计算实体的方向
drone.orientation = new Cesium.VelocityOrientationProperty(drone.position);
// 对实体的位置 position 进行插值,平滑路径轨迹
drone.position.setInterpolationOptions({
interpolationDegree : 3,
interpolationAlgorithm : Cesium.HermitePolynomialApproximation
});
});
/**
* 加载 3D Tile 数据
* Cesium 提供 Cesium3DTileset 类来解析 3D Tile 数据
*
*/
//加载 Cesium Ion 中的纽约市的3D模型
var city = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({ url: Cesium.IonResource.fromAssetId(75343) }));
var defaultStyle = new Cesium.Cesium3DTileStyle({
color : "color('white')",
show : true
});
var transparentStyle = new Cesium.Cesium3DTileStyle({
color : "color('white', 0.3)",
show : true
});
var heightStyle = new Cesium.Cesium3DTileStyle({
color : {
//根据要素属性值进行条件渲染
conditions : [
["${Height} >= 300", "rgba(45, 0, 75, 0.5)"],
["${Height} >= 200", "rgb(102, 71, 151)"],
["${Height} >= 100", "rgb(170, 162, 204)"],
["${Height} >= 50", "rgb(224, 226, 238)"],
["${Height} >= 25", "rgb(252, 230, 200)"],
["${Height} >= 10", "rgb(248, 176, 87)"],
["${Height} >= 5", "rgb(198, 106, 11)"],
["true", "rgb(127, 59, 8)"]
]
}
});
city.style = heightStyle;
//绑定样式切换事件
var tileStyle = document.getElementById('tileStyle');
function set3DTileStyle() {
var selectedStyle = tileStyle.options[tileStyle.selectedIndex].value;
if (selectedStyle === 'none') {
city.style = defaultStyle;
} else if (selectedStyle === 'height') {
city.style = heightStyle;
} else if (selectedStyle === 'transparent') {
city.style = transparentStyle;
}
}
tileStyle.addEventListener('change', set3DTileStyle);
/**
* 事件交互
* 通过 Scene.pick/Scene.drillPick/Globe.pick 可以获取鼠标点击的位置下的实体。
*
* 事件处理:
* 通过 ScreenSpaceEventHandler 类处理用户输入事件。
* 构造器new Cesium.ScreenSpaceEventHandler(element)
* 主要方法:
* setInputAction(action, type, modifier)
* action事件回调函数
* type事件类型可选值参考https://cesium.com/docs/cesiumjs-ref-doc/ScreenSpaceEventType.html
* 常用 Cesium.ScreenSpaceEventType.LEFT_CLICK鼠标左键单击
* Cesium.ScreenSpaceEventType.MOUSE_MOVE鼠标移动
*/
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
var previousPickedEntity = undefined;
handler.setInputAction(function(movement) {
var pickedPrimitive = viewer.scene.pick(movement.endPosition);
var pickedEntity = (Cesium.defined(pickedPrimitive)) ? pickedPrimitive.id : undefined;
// 取消高亮的实体
if (Cesium.defined(previousPickedEntity)) {
previousPickedEntity.billboard.scale = 1.0;
previousPickedEntity.billboard.color = Cesium.Color.WHITE;
}
// 高亮当前鼠标下的实体
if (Cesium.defined(pickedEntity) && Cesium.defined(pickedEntity.billboard)) {
pickedEntity.billboard.scale = 2.0;
pickedEntity.billboard.color = Cesium.Color.ORANGERED;
previousPickedEntity = pickedEntity;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
/**
* 控制 相机 模式
* viewer.trackedEntity :视角追踪实体的位置
*/
var freeModeElement = document.getElementById('freeMode');
var droneModeElement = document.getElementById('droneMode');
// Create a follow camera by tracking the drone entity
function setViewMode() {
if (droneModeElement.checked) {
viewer.trackedEntity = drone;
} else {
viewer.trackedEntity = undefined;
viewer.scene.camera.flyTo(homeCameraView);
}
}
freeModeElement.addEventListener('change', setViewMode);
droneModeElement.addEventListener('change', setViewMode);
viewer.trackedEntityChanged.addEventListener(function() {
if (viewer.trackedEntity === drone) {
freeModeElement.checked = false;
droneModeElement.checked = true;
}
});
//-----------------------------------------------------------
var neighborhoodsElement = document.getElementById('neighborhoods');
neighborhoodsElement.addEventListener('change', function (e) {
neighborhoods.show = e.target.checked;
});
var loadingIndicator = document.getElementById('loadingIndicator');
loadingIndicator.style.display = 'block';
city.readyPromise.then(function () {
loadingIndicator.style.display = 'none';
});
</script>
</body>
</html>