<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta name="description"
content="Layer imagery from multiple sources, including WMS servers, Bing Maps, ArcGIS Online, OpenStreetMaps, and more, and adjust the alpha of each independently.">
<meta name="cesium-sandcastle-labels" content="Showcases">
<title>Imagery Layers Manipulation</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
<script type="text/javascript" src="../../../ThirdParty/requirejs-2.1.20/require.js"></script>
<script type="text/javascript">
if (typeof require === 'function') {
require.config({
baseUrl: '../../../Source',
waitSeconds: 120
});
}
</script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
@import url(../templates/bucket.css);
#toolbar {
background: rgba(42, 42, 42, 0.8);
padding: 4px;
border-radius: 4px;
}
#toolbar input {
vertical-align: middle;
padding-top: 2px;
padding-bottom: 2px;
}
#toolbar table tr {
transform: translateY(0);
transition: transform 0.4s ease-out;
}
#toolbar table tr.up {
transform: translateY(33px);
transition: none;
}
#toolbar table tr.down {
transform: translateY(-33px);
transition: none;
}
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<table>
<!--
知识点:
data-bind的值的格式类似JSON,键是标签本身的属性,值的viewModel的属性或遍历对象的属性或css中绑定该元素的类名。
键值对之间用“,”分隔,键值之间用“:”分隔,可以嵌套。
在html控件中,用$parent代表与其绑定的视图模型viewModel,借此可以访问viewModel的属性或方法。
用$data,代表foreach:后边循环的每一个元素的值,此处是layer。
用$index(),代表foreach:后边循环的每一个元素在当前数组的下标。
其中input标签的data-bind值中,可以用类似valueUpdate: 'input'键值对,规定该值的更新方式。
data-bind的值的键类别统计如下:
foreach, css, checked, text, visible, options, optionsText, value, valueUpdate, click。
其中,click是js里的动作。css, 是UI用户界面的动作。-->
<!--对viewModel.layers数组进行循环。-->
<tbody data-bind="foreach: layers">
<!--$parent绑定viewModel,$data绑定循环的当前layer。css的动态样式up/down,哪个为true哪个执行。-->
<tr data-bind="css: { up: $parent.upLayer === $data, down: $parent.downLayer === $data }">
<!--复选框,绑定当前layer的属性show。-->
<td><input type="checkbox" data-bind="checked: show"></td>
<td>
<!--绑定当前layer的属性name。-->
<span data-bind="text: name, visible: !$parent.isSelectableLayer($data)"></span>
<!--绑定viewModel.baseLayers(层的数组),选项文本是层名,值是选中的图层。-->
<select data-bind="visible: $parent.isSelectableLayer($data), options: $parent.baseLayers, optionsText: 'name', value: $parent.selectedLayer"></select>
</td>
<td>
<!--绑定当前layer的属性alpha,值更新方式,input事件,即,当input的value值发生变化时就会触发,不用等到失去焦点。-->
<input type="range" min="0" max="1" step="0.01" data-bind="value: alpha, valueUpdate: 'input'">
</td>
<td>
<!--升层按钮,绑定单击事件,单击一次提升一层,可见性,该层可以升层。-->
<button type="button" class="cesium-button"
data-bind="click: function() { $parent.raise($data, $index()); }, visible: $parent.canRaise($index())">
▲
</button>
</td>
<td>
<!--降层按钮,绑定单击事件,单击一次降低一层,可见性,该层可以降层。-->
<button type="button" class="cesium-button"
data-bind="click: function() { $parent.lower($data, $index()); }, visible: $parent.canLower($index())">
▼
</button>
</td>
</tr>
</tbody>
</table>
</div>
<script id="cesium_sandcastle_script">
function startup(Cesium) {
'use strict';
// Sandcastle_Begin
let viewer = new Cesium.Viewer('cesiumContainer', {
baseLayerPicker: false // 不创建“基层拾取器”。
});
let imageryLayers = viewer.imageryLayers;
console.log("imageryLayers:", imageryLayers);
// 1.创建viewModel对象
let viewModel = {
layers: [], // 该数组要存放的图层,和imageryLayers中的图层,数量相同,顺序相反。
baseLayers: [], // baseLayers,包含待选择的所有的基层,选择某个基层后,会用该基层替换掉视图imageryLayers中原来的基层。
upLayer: null,
downLayer: null,
selectedLayer: null, // 当前选择的基层。
isSelectableLayer: function (layer) {
/*此函数用于,判断该层是否是下拉框可选的基层。*/
return this.baseLayers.indexOf(layer) >= 0;
},
raise: function (layer, index) {
/*此函数用于,使某图层的堆叠顺序,向上提升一层。*/
imageryLayers.raise(layer);
viewModel.upLayer = layer;
viewModel.downLayer = viewModel.layers[Math.max(0, index - 1)];
updateLayerList();
window.setTimeout(function () {
viewModel.upLayer = viewModel.downLayer = null;
}, 10);
},
lower: function (layer, index) {
/*此函数用于,使某图层的堆叠顺序,向下下降一层。*/
imageryLayers.lower(layer);
viewModel.upLayer = viewModel.layers[Math.min(viewModel.layers.length - 1, index + 1)];
viewModel.downLayer = layer;
updateLayerList();
window.setTimeout(function () {
viewModel.upLayer = viewModel.downLayer = null;
}, 10);
},
canRaise: function (layerIndex) {
/*此函数用于,判断某图层的堆叠顺序,可以向上提升一层吗?
* layerIndex,该图层在viewModel.layers中的下标。*/
return layerIndex > 0;
},
canLower: function (layerIndex) {
/*此函数用于,判断某图层的堆叠顺序,可以向下下降一层吗?
* layerIndex, 该图层在viewModel.layers中的下标。*/
return layerIndex >= 0 && layerIndex < imageryLayers.length - 1;
}
};
let baseLayers = viewModel.baseLayers;
// 2.监测viewModel中的属性
Cesium.knockout.track(viewModel);
// 创建图层(基层、附加层)并添加到视图。
setupLayers();
// 把imageryLayers中的图层,倒序同步到viewModel.layers数组中。
updateLayerList();
// 3.将viewModel对象与html控件绑定
let toolbar = document.getElementById('toolbar');
Cesium.knockout.applyBindings(viewModel, toolbar);
// 4.监听viewModel的selectedLayer属性。
// 该属性的变更,是客户端动态操作,并同步到viewModel的selectedLayer属性。
// 从而可以实现,对该属性的监听,动态操作视图中的基层的变更。
Cesium.knockout.getObservable(viewModel, 'selectedLayer').subscribe(function (baseLayer) {
// 以下要处理对下拉基层选择器的更改。baseLayer,刚选择的基层。
// 1.找到当前视图中基层在viewModel.layers中的下标,保存到activeLayerIndex。
let activeLayerIndex = 0;
let numLayers = viewModel.layers.length;
for (let i = 0; i < numLayers; ++i) {
if (viewModel.isSelectableLayer(viewModel.layers[i])) {
activeLayerIndex = i;
break;
}
}
// activeLayer,当前基层。
let activeLayer = viewModel.layers[activeLayerIndex];
// 获取当前基层的属性show, alpha.
let show = activeLayer.show;
let alpha = activeLayer.alpha;
// remove(layer, destroy) → Boolean
imageryLayers.remove(activeLayer, false);
// add(layer, index)
// 把更改后的基层,添加到视图中,相应的下标位置。
// 此处numLayers - 1 - activeLayerIndex,是根据该图层在viewModel.layers中的位置,
// 计算该图层应该在imageryLayers数组中的位置。
imageryLayers.add(baseLayer, numLayers - activeLayerIndex - 1);
// 可以先添加,再设置该层的属性。
baseLayer.show = show;
baseLayer.alpha = alpha;
updateLayerList();
});
/*以下是工具函数。*/
function setupLayers() {
/*此函数用于,创建各种层。*/
// 1.创建基层。
addBaseLayerOption('Bing Maps Aerial', undefined); // 当前基层。
addBaseLayerOption(
'Bing Maps Road',
new Cesium.BingMapsImageryProvider({
url: 'https://dev.virtualearth.net',
mapStyle: Cesium.BingMapsStyle.ROAD
}));
addBaseLayerOption(
'ArcGIS World Street Maps',
new Cesium.ArcGisMapServerImageryProvider({
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer'
}));
addBaseLayerOption(
'OpenStreetMaps',
Cesium.createOpenStreetMapImageryProvider());
addBaseLayerOption(
'MapQuest OpenStreetMaps',
Cesium.createOpenStreetMapImageryProvider({
url: 'https://otile1-s.mqcdn.com/tiles/1.0.0/osm/'
}));
addBaseLayerOption(
'Stamen Maps',
Cesium.createOpenStreetMapImageryProvider({
url: 'https://stamen-tiles.a.ssl.fastly.net/watercolor/',
fileExtension: 'jpg',
credit: 'Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under CC BY SA.'
}));
addBaseLayerOption(
'Natural Earth II (local)',
Cesium.createTileMapServiceImageryProvider({
url: Cesium.buildModuleUrl('Assets/Textures/NaturalEarthII')
}));
addBaseLayerOption(
'USGS Shaded Relief (via WMTS)',
new Cesium.WebMapTileServiceImageryProvider({
url: 'http://basemap.nationalmap.gov/arcgis/rest/services/USGSShadedReliefOnly/MapServer/WMTS',
layer: 'USGSShadedReliefOnly',
style: 'default',
format: 'image/jpeg',
tileMatrixSetID: 'default028mm',
maximumLevel: 19,
credit: 'U. S. Geological Survey'
}));
// 2.以下要创建附加层,并添加到视图,后来居上。
addAdditionalLayerOption(
'United States GOES Infrared',
new Cesium.WebMapServiceImageryProvider({
url: 'https://mesonet.agron.iastate.edu/cgi-bin/wms/goes/conus_ir.cgi?',
layers: 'goes_conus_ir',
credit: 'Infrared data courtesy Iowa Environmental Mesonet',
parameters: {
transparent: 'true',
format: 'image/png'
}
})
);
addAdditionalLayerOption(
'United States Weather Radar',
new Cesium.WebMapServiceImageryProvider({
url: 'https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi?',
layers: 'nexrad-n0r',
credit: 'Radar data courtesy Iowa Environmental Mesonet',
parameters: {
transparent: 'true',
format: 'image/png'
}
})
);
addAdditionalLayerOption(
'TileMapService Image',
Cesium.createTileMapServiceImageryProvider({
url: '../images/cesium_maptiler/Cesium_Logo_Color'
}),
0.2
);
addAdditionalLayerOption(
'Single Image',
new Cesium.SingleTileImageryProvider({
url: '../images/Cesium_Logo_overlay.png',
rectangle: Cesium.Rectangle.fromDegrees(-115.0, 38.0, -107, 39.75)
}),
1.0
);
addAdditionalLayerOption(
'Grid',
new Cesium.GridImageryProvider(), 1.0, false
);
addAdditionalLayerOption(
'Tile Coordinates',
new Cesium.TileCoordinatesImageryProvider(), 1.0, false
);
}
function addBaseLayerOption(name, imageryProvider) {
/*此函数用于,创建基层,并添加到viewModel.baseLayers数组。*/
let layer;
if (typeof imageryProvider === 'undefined') {
layer = imageryLayers.get(0);
// 初始化当前基层。
viewModel.selectedLayer = layer;
} else {
layer = new Cesium.ImageryLayer(imageryProvider);
}
layer.name = name;
baseLayers.push(layer);
}
function addAdditionalLayerOption(name, imageryProvider, alpha, show) {
/*此函数用于,创建附加层,添加到视图,并为其设置alpha, show, name属性。*/
let layer = imageryLayers.addImageryProvider(imageryProvider);
layer.alpha = Cesium.defaultValue(alpha, 0.5);
layer.show = Cesium.defaultValue(show, true);
layer.name = name;
// 监听该附加层的alpha,show,name属性。
Cesium.knockout.track(layer, ['alpha', 'show', 'name']);
}
function updateLayerList() {
/*此函数用于,清空viewModel.layers数组,
并把imageryLayers中的图层,倒序添加到viewModel.layers数组中。
这是为了实现,在html控件上,上层的图层在控件上边显示 的效果。
当创建并添加图层、移动图层、变更基层时,需要调用此函数,
以确保视图图层和viewModel中图层状态的同步。*/
let numLayers = imageryLayers.length;
// 清空viewModel.layers数组。
viewModel.layers.splice(0, viewModel.layers.length);
for (let i = numLayers - 1; i >= 0; --i) {
viewModel.layers.push(imageryLayers.get(i));
}
}
//Sandcastle_End
Sandcastle.finishedLoading();
}
if (typeof Cesium !== 'undefined') {
startup(Cesium);
} else if (typeof require === 'function') {
require(['Cesium'], startup);
}
</script>
</body>
</html>