最近項目上遇到一個專題樹,遇到打開專題的時間軸後,以前打開的圖層會被關閉的bug過程是這樣的,先打開增城區規劃導則地塊
,而後再打開上面含有多個年份的歷史圖層,規劃導則地塊
,這個時候增城區規劃導則地塊
的圖層會被關閉。web
通過代碼定位和按行註釋,發現是這段代碼針對圖層的關閉和顯示有問題:json
// 處理默認年份的顯示隱藏
var layer = this.defaultLayer;
if (visible) {
`layer.setVisibleLayers && layer.setVisibleLayers([layerobj.layerindex])`;
layer.setVisibility(true);
} else {
// 關閉
`layer.setVisibleLayers && layer.setVisibleLayers([]);`
layer.setVisibility(false);
}
複製代碼
爲何當前的 layer
會把其餘的圖層關閉了呢?下面先理解幾個概念。api
本文主要講述如下幾點內容:緩存
圖層是 ArcMap、ArcGlobe 和 ArcScene 等Arcgis 產品套件中地理數據集的顯示機制。一個圖層引用一個數據集,並指定如何利用符號和文本標註繪製該數據集。向地圖添加圖層時,要指定它的引用數據集並設定地圖符號和標註屬性。bash
包含一個地圖控件的每一個應用程序是經過一系列圖層組裝的。顯示以特定的順序顯示在地圖上,列在最底部的顯示在地圖的最上面顯示,也就是先添加的顯示在下面顯示(原理相似於「棧」)運維
全部的圖層都是從Layer類型繼承而來的,能夠參考下載的API中的對象模型圖。測試
Layer
|–TiledMapServiceLayer
|----|–ArcGISTiledMapServiceLayer
|–DynamicLayer
|----|–DynamicMapServiceLayer
|----------|–ArcGISDynamicMapServiceLayer
|----------|–ArcGISImageServiceLayer
|----------|–GPResultImageLayer
|–GraphicsLayer
|----|–FeatureLayer
|–ElementLayer
複製代碼
而圖層是怎麼加載出來的呢,它是經過地圖服務加載出來的。優化
地圖服務是一種利用 ArcGIS 使地圖可經過 web 進行訪問的方法。咱們首先在 ArcMap 中製做地圖,而後將地圖發佈到 ArcGIS Server 站點上。當地圖服務發佈成功後,咱們能夠經過網址(xxxx/arcgis/rest/services)來查看地圖服務所支持的操做,地圖服務所包含的數據,以及咱們還能夠經過網址來測試地圖服務的功能。ui
以後在Web 應用程序、ArcGIS for Desktop、ArcGIS Online 以及其餘客戶端應用程序中請求該地址使用此地圖服務this
下面說說常見的兩種圖層加載模式,實例化一個圖層對象,須要傳入圖層的 url
。
原理:切片服務是已經經過比例尺切好地圖了,如一般的底圖,通常是切片服務加載的,當你經過鼠標放大底圖,它會根據當前的比例尺來加載已經切好的圖片,加載的方式是經過
export
接口請求已經切好的圖片。
因爲切片服務已經切好了,因此沒法經過相似setVisibleLayers
來控制它的圖層顯示,只能經過setVisiblity
控制整個圖層的顯示。導出圖片時,export
會把整個當前比例的切片導出來。
一個發佈出來的切片以下:
經過ArcGISTiledMapServiceLayer
新建一個切片類實例,而後加載到地圖中
var layer = new ArcGISTiledMapServiceLayer(layerobj.url);
map.addLayer(layer);
複製代碼
控制切片圖層的顯示
// 設置圖層顯示/隱藏
layerVisibleRefreshByName: function(obj) {
if (obj == null) return;
var serviceid = obj.serviceid;
var layername = obj.layername;
var layervisible = obj.layervisible;
// 若是 serviceid 不存在時,運維賦值爲 label 名稱
if (serviceid == this.guid || serviceid == this.label) {
this.setVisibility(layervisible);
}
}
複製代碼
固然,若是切片服務在發佈時,勾選了可使用動態服務加載的,那麼切片服務也能夠經過ArcGISDynamicMapServiceLayer
來加載。
原理:
一個動態服務的信息以下:
經過ArcGISDynamicMapServiceLayer
新建一個動態類實例,而後加載到地圖中
var layer = new ArcGISDynamicMapServiceLayer(layerobj.url);
map.addLayer(layer);
複製代碼
在咱們執行setVisibleLayers
時,會經過 export
方式來把對應的子圖層輸出圖片,而後加載到地圖中。setVisibleLayers(-1)
關閉全部子圖層。
控制動態圖片的顯示
/** * 改變圖層的可見性 * @param {Object} dlayer 服務圖層 * @param {Number} layerid 子圖層 id * @param {Boolean} layervisible 可見性 */
changeLayerVisible: function (dlayer, layerid, layervisible) {
if (dlayer == null) return;
if (layerid < 0) return;
var arrc = dlayer.visibleLayers;
arrc = this.dealWithLayerInfos(arrc, dlayer.layerInfos);
if (arrc == null || (arrc.length == 1 && arrc[0] == -1)) {
arrc = [];
}
if (layervisible) {
if (!this.checkLayerId(arrc, layerid)) {
arrc.push(layerid);
}
} else {
if (this.checkLayerId(arrc, layerid)) {
arrc = this.removeLayerId(arrc, layerid);
}
}
this.setVisibleLayers(arrc, true);
},
checkLayerId: function (arrc, layerid) {
if (arrc == null) return false;
for (var i = 0; i < arrc.length; i++) {
if (arrc[i] == layerid) {
return true;
}
}
return false;
},
複製代碼
說到歷史時間軸,首先要理解專題樹裏面的節點信息。
專題樹由專題組成,每個葉子節點都是一個專題,那麼專題是什麼呢?
專題:專題也就是一個圖層服務,每個專題layer
都是系統初始化時,經過動態服務或切片服務實例化後添加到地圖中的, 每一個專題圖層,都有一個圖層組 layers
,這個是該專題服務下的默認顯示的子圖層集合,因此打開或關閉專題時,若是子圖層是經過動態服務加載的,也就是關閉對應的子圖層。若是是切片服務的,則是關閉整個服務對象。
關於歷史時間軸的邏輯是這樣的
{
"type": "規劃導則地塊",
"layers": [
{
"label": "2019",
"layertype": "dynamic",
"layername": "規劃導則地塊",
"layerindex": 4,
"serviceName": "控制性規劃導則",
"serviceUid": "",
"defaultLayer": true,
"url": "xxxxx/arcgis/rest/services/%E6%8E%A7%E5%88%B6%E6%80%A7%E8%A7%84%E5%88%92%E5%AF%BC%E5%88%99/MapServer/4"
},
{
"label": "2018",
"layertype": "dynamic",
"layername": "規劃導則地塊",
"layerindex": 4,
"serviceName": "控制性規劃導則",
"serviceUid": "",
"defaultLayer": false,
"url": "xxxxx/arcgis/rest/services/%E6%8E%A7%E5%88%B6%E6%80%A7%E8%A7%84%E5%88%92%E5%AF%BC%E5%88%992018/MapServer"
},
{
"label": "2017",
"layertype": "dynamic",
"layername": "規劃導則地塊",
"layerindex": 4,
"serviceName": "控制性規劃導則2017",
"defaultLayer": false,
"url": "xxxxx/arcgis/rest/services/%E6%8E%A7%E5%88%B6%E6%80%A7%E8%A7%84%E5%88%92%E5%AF%BC%E5%88%992017/MapServer"
},
{
"label": "2016",
"layertype": "dynamic",
"layername": "規劃導則地塊2016",
"layerindex": 4,
"serviceName": "控制性規劃導則2016",
"defaultLayer": false,
"url": "xxxxx/arcgis/rest/services/%E6%8E%A7%E5%88%B6%E6%80%A7%E8%A7%84%E5%88%92%E5%AF%BC%E5%88%992016/MapServer"
}
]
},
複製代碼
/** * _changeLayerVisible() 改變圖層的顯示狀況 * @param {Object} layerobj 圖層信息 * @param {Boolean} visible 是否可見 */
_changeLayerVisible: function(layerobj, visible) {
this.map = window._map; // 獲取到地圖
if (!layerobj) {
return;
}
if (layerobj.label != this.clashHisdata.label) {
// 隔離與默認年份相沖突的年份,把默認年份放在else中處理
var layerId =
this.defaultLayer.label + '_historydataservice' + layerobj.label; // 用於同時添加多個圖層 多個圖層會出現
topic.publish('history-layerIds', layerId); // 歷史圖層id傳送至專題樹,由專題書統一管理圖層開關
this.historyLayerIds.push(layerId);
switch (layerobj.layertype) {
case 'tiled':
if (visible) {
var layer = this.map.getLayer(layerId);
if (layer) {
this.map.removeLayer(layer);
layer = new ArcGISTiledMapServiceLayer(layerobj.url);
this._currentLayer = layer;
layer.id = layerId;
this.map.addLayer(layer);
this.map.reorderLayer(layer, 1);
layer.setVisibility(true);
} else {
layer = new ArcGISTiledMapServiceLayer(layerobj.url);
this._currentLayer = layer;
layer.id = layerId;
this.map.addLayer(layer);
this.map.reorderLayer(layer, 1);
}
this._currentLayer.setOpacity(this._opacity);
} else {
var layer = this.map.getLayer(layerId);
if (layer) {
//this.map.removeLayer(layer);
layer.setVisibility(false);
}
}
break;
case 'dynamic':
if (visible) {
var layer = this.map.getLayer(layerId);
if (layer) {
if (layer.url == layerobj.url) {
layer.setVisibleLayers([layerobj.layerindex]);
this._currentLayer = layer;
layer.setVisibility(true);
} else {
this.map.removeLayer(layer);
layer = new ArcGISDynamicMapServiceLayer(layerobj.url);
this._currentLayer = layer;
layer.id = layerId;
this.map.addLayer(layer);
this.map.reorderLayer(layer, 1);
layer.setVisibleLayers([layerobj.layerindex]);
layer.setVisibility(true);
}
} else {
var dlayer = new ArcGISDynamicMapServiceLayer(layerobj.url);
this._currentLayer = dlayer;
dlayer.id = layerId;
this.map.addLayer(dlayer);
this.map.reorderLayer(dlayer, 1);
dlayer.setVisibleLayers([layerobj.layerindex]);
}
this._currentLayer.setOpacity(1);
setTimeout(
lang.hitch(this, function() {
this._currentLayer.setOpacity(this._opacity);
}),
200
);
} else {
var layer = this.map.getLayer(layerId);
if (layer) {
layer.setVisibleLayers([]);
layer.setVisibility(false);
}
}
break;
}
} else {
`// 處理默認年份的顯示隱藏 var layer = this.defaultLayer; if (visible) { layer.setVisibleLayers && layer.setVisibleLayers([layerobj.layerindex])`;
layer.setVisibility(true);
} else {
// 關閉
layer.setVisibleLayers && layer.setVisibleLayers([]);
layer.setVisibility(false);
}
}
},
複製代碼
Layer: 一個圖層服務包含了不少子圖層
這個是歷史面板初始化時的操做。 時間軸打開後,經過serviceid
去獲取添加到地圖中的當前 layer
對象。 從專題信息中,獲取到當前專題裏面的圖層,默認顯示的子圖層。 再經過它的serviceUid
獲取到加載到地圖中的父圖層(專題)。
圖層關閉由 setVisibility(isVisible)
和 setVisibleLayers(ids, doNotRefresh?)
組合控制,由切片服務生成的圖層只有 setVisibility
屬性,動態服務生成的圖層則由二者組合控制圖層的顯示。如圖,setVisibility
控制整個圖層的顯示,而 setVisibleLayers
能夠更加細粒度地控制圖層裏面的子圖層。
再看看以前的代碼實現,經過斷點發現,默認圖層 layer
的子圖層包含了增城導則地塊圖層
,所以在打開默認圖層的某個子圖層時,這行代碼layer.setVisibleLayers([layerobj.layerindex])
只賦值了當前子圖層的索引id,致使把以前的子圖層都關閉了。
// 處理默認年份的顯示隱藏
var layer = this.defaultLayer;
if (visible) {
`layer.setVisibleLayers && layer.setVisibleLayers([layerobj.layerindex])`;
layer.setVisibility(true);
} else {
// 關閉
`layer.setVisibleLayers && layer.setVisibleLayers([]);`
layer.setVisibility(false);
}
複製代碼
這時候只須要添加對應的圖層服務類型判斷邏輯,緩存以前的就能夠了。
// 處理默認年份的顯示隱藏
var layer = this.defaultLayer;
```var visibleLayers = []; // 可見的圖層 visibleLayers = visibleLayers.concat(layer.visibleLayers);```
if (visible) {
// 打開
var index = visibleLayers.indexOf(layerobj.layerindex);
if (index === -1 && layerobj.layertype !== 'tiled') {
visibleLayers.push(layerobj.layerindex); // 添加新的圖層索引進去,不然傳遞 -1 會關閉全部圖層
layer.setVisibleLayers && layer.setVisibleLayers(visibleLayers); // 注意區分tiled和dynamic
}
layer.setVisibility(true);
} else {
// 知足:
// 1. 須要解決時間軸後面的Layer 被默認的 Layer覆蓋問題,
// 2. 可是不能把整個 Layer 關閉了,不然,會影響屬於同一個圖層服務實例下,其餘子圖層的顯示。
// 3. 當前只能把後面的圖層移動 z-index,可是又要知足時間軸的圖層做爲底圖來使用。
`if (layerobj.layertype === 'tiled') { // 判斷是否爲切片 layer.setVisibility(false); } else { // 動態圖片服務的關閉 // 關閉 var index = visibleLayers.indexOf(layerobj.layerindex); if (index > -1) { visibleLayers.splice(index, 1); } layer.setVisibleLayers && layer.setVisibleLayers(visibleLayers); }`
}
}
複製代碼
固然,上面的默認圖層的顯示/隱藏,能夠直接把圖層對象傳遞給專題樹來統一處理開關,這樣就不用寫這些判斷邏輯了。
(全文完)
arcgis api
是通過優化的,只有地圖範圍改變了,纔會去請求地圖服務。因此在切換時間軸時,要注意改變它的範圍,才能更好的定位錯誤。