# 场景整合
# 适用场景
多场景下,地图不重新加载
# 示例
# 功能基础
- 框架 - 图层列表 - 支持动态增、删、改图层
- 框架 - 图例 - 支持动态修改
- 框架 - 时间轴 - 支持动态修改
# 实现逻辑
# 基础
- 多场景有 同一个坐标系和初始范围
- 每个场景都有 唯一 的标识,场景唯一标识由大屏维护
# 整合
# 场景
- 保证底图和地图初始配置和主体应用相同
- 在开发者代码中通过特定代码监听场景销毁的消息,一但发现要销毁的是自己,断开和大屏的联系,销毁所有监听事件和代码中加载的图层(保证自己的场景不再影响地图)
# 主体
# 场景加载
通过大屏发送过来的要加载场景的预览地址,动态读取场景的图层配置、图例配置、时间轴配置,在现有的基础上以增的方式添加场景,并把场景的开发者代码执行一遍
# 场景销毁
- 通过已读取的图层配置、图例配置、时间轴配置删除要销毁场景的图层、图例、时间轴
- 通过在主体应用中向全局发送销毁场景的唯一标识,让所有的场景能接收到销毁的场景是谁
# 大屏
- 首次加载场景:
- 大屏等待地图发出地图加载完成的消息后,首次把要加载的场景信息发给地图并保存当前场景标识
- 点击场景按钮:
- 向地图发送销毁当前场景,加载点击场景的消息,并把当前场景变量置为按钮场景
# 相关说明
# 底图相关
因为不同坐标系不同切片方案的影像服务是不好相互叠加的。因此,需要一开始就明确统一场景的底图服务和坐标系。
这些可以在 主体应用 中配置,然后告知其他场景的开发人员,防止因为底图相关问题导致返工。
地图初始范围同理。
# 公共要素
如果要添加公共要素的话,有两种实现方式:
可以在 主体应用 中的直接配置和开发
把公共要素作为一个场景,在 主体应用 准备好接收大屏消息后,第一个加载,不移除
# 图层命名
在 MapGO 平台中 配置图层时,要保证图层名称和图层分类名称的唯一性,统一使用 专题-场景-图层含义 对图层进行中文命名,如:大气-预报-监测站点
# 消息命名
在 MapGO 平台中 的开发者中编写代码时,如需自定义消息/事件名,要保证消息/事件名称的唯一性。统一使用 专题-场景-消息/事件含义 对 消息/事件进行命名:如 水环境质量现状图层过滤 消息/事件的可命名为:water_qualitystatus_layerfilter。
# 代码
# 主体
/**
* 场景整合
* @module
* @author suyp
* @date 2021-3-20
* @version 1.0
*/
var webSocketURL = "http://192.168.0.140:7070/view"; // 大屏地址 IP 和 端口 改为要连接大屏的 IP 和 端口
// getQueryVariable 是根据window.location 去获取的参数
var clientCode = getQueryVariable("wsClientCode")
? getQueryVariable("wsClientCode")
: "thsgis_"; // 获取大屏传过来的跨屏交互ID
var mapGoConstants = {
// MapGo 平台相关常量
userName: "userName", // 应用文件路径中代表用户名的key
appType: "appType", // 应用文件路径中代表应用类型的key
appID: "appID", // 应用文件路径中代表应用在该用户该应用类型下唯一标识的key(旧版为appName)
middleUrl: "/mapgo/apps/", // 应用配置文件和IP端口中间路径部分
configAddress: {
// 平台-应用配置文件的固定地址片段
layerManager: "/LayerManager/config.json", // 图层配置
timeLine: "/TimeLineManager/config.json", // 时间轴配置
legendManager: "/LegendManager/config.json", // 图例配置
interactions: "/interactions.js", // 开发者代码
},
interactions: {
// 开发者代码相关
// 新旧两个能提取用户开发者代码的关键字符串
oldDefault:
"subscribeEvent('allLayersLoaded',function(){unsubscribe(alllayerLoadedHandle);",
newestDefault: "subscribeEvent('allLayersLoaded',function(){",
},
};
var sceneIntegration = {
// 场景整合相关常量
init: "scene_init", // 场景初始化的key
destroy: "scene_destroy", // 销毁场景的key
gisMessageKey: "thsGis", // 标识是发给GIS的消息(如果不用可修改相关位置)
mapLoadMessage: {
// websocket连接后,通知大屏GIS已经准备好接收消息了
type: "runInteractive",
data: [
{
shareCode: "mapLoaded",
runtimeValue: "true",
},
],
},
error: {
// 相关错误提示
initScene: "要加载的场景已存在或者场景地址不存在!",
interactions: "开发者代码错误:",
replaceJSONStr: "JSON替换失败:",
},
};
var topicEvents = {
// 二维发布订阅的消息key集合
layersUpdated: "layers-updated", // 图层列表-复数图层添加/更新/移除完成
customLayersUpdated: "layers-updated-custom", // 自定义复数图层操作完成
};
var customWidgetType = {
// 自定义微件的类型
legend: "legend", // 图例
timeLine: "time-line", // 时间轴
};
var allLayers = {}; // 初始图层列表的图层
var curScensePolygonLayers = []; // 要调整顺序的面图层数据,记得调整完清空
var mapPolygonLayerLength = 0; // 当前地图面图层的数量
var curSceneLayers = []; // 当前场景全部图层转为一级后的数组
var isAdd = true; // 第一次加载场景的时候,需要合并初始图层列表图层和场景图层,用来标识是否是第一次
var initLayers = []; // 初始地图所有图层id的数组,由开始时从 map._layers 获取
// 用于替换场景配置文件的字符,适用于服务迁移时IP等改变、引用文件路径变更等
// 因为在vue中也有替换字符的代码,所以这里的地址需要加上特定字符来保证不会被vue替换为新的地址(如果确定没有vue参与,可以不加特殊字符)
var replaceResources = [
// {
// replaceStr: '"..\/icons', // 要替换的字符串
// replacedStr: '"http://' + environmentalParam.gisFrameWorkSocket + '/gis_framework/icons', // 替换后的字符串
// },
];
var sceneList = []; // 场景对象集合
var layers = []; // 所有的图层
var sceneOb = null; // 最新场景的配置
var handlingScene = []; // 处理场景的命令缓冲区
// 获取初始图层列表配置,用来和第一次加载的场景的图层合并配置
if (
layerManager.layerMangerInstance &&
layerManager.layerMangerInstance.getLayersConfig
) {
var layerConfig = layerManager.layerMangerInstance.getLayersConfig();
if (layerConfig && typeof layerConfig.layers === "object") {
allLayers = layerConfig.layers;
}
}
//!------------ 大屏消息处理 ------------!//
/**
* 初始化webSocket连接
* @param {string} clientCode 跨屏交互ID
* @param {Function} receiveCallback 接受消息的回调
* @param {Function} closeCallback 关闭连接的回调
* @param {string} webSocketURL webSocket服务器地址
* @description sceneCoordinate 为场景整合模块在webSocket中的ket
*/
var thsMapSocket = ths.initSocket(
clientCode,
{
sceneCoordinate: function(content) {
content = JSON.parse(content.data).data;
// 先通过 { 初步判断是json格式
if (content.indexOf("{") > -1) {
// 和大屏人员确定过,给GIS发的消息中都会带有thsGis关键字,不是给gis发的消息,不处理(可以根据实际情况修改)
var sceneProArray = JSON.parse(content).data;
if (content.indexOf(sceneIntegration.gisMessageKey) > -1) {
// 单场景-场景初始化和销毁消息合一
var operations = JSON.parse(sceneProArray[0].runtimeValue);
// 场景操作信息,包含销毁和加载
var sceneOperation = {};
// 传过来的应该是个数组
if (isArray(operations)) {
//!-----场景加载和销毁-------!//
operations.forEach(function(pro) {
if (pro.messageType === sceneIntegration.init) {
sceneOperation.init = pro;
}
if (pro.messageType === sceneIntegration.destroy) {
sceneOperation.destroy = pro;
}
});
// 将待处理操作放入命令缓冲区
handlingScene.push(sceneOperation);
// 因为处理场景的命令是执行完一个才会执行下一个,所以,在接收到消息并把消息放到命令缓冲区中后,
// 只有一个待执行命令的时候才会在这里开始处理命令,其他的时候会在 "layers-updated-custom" 订阅的回调中继续执行
if (handlingScene.length === 1) {
// 开始处理场景
handleScene(sceneOperation);
}
}
}
}
},
},
null,
webSocketURL,
function() {
// 向大屏发送地图加载完成的消息,大屏收到后向地图发送要加和销毁什么场景
sendMessage(sceneIntegration.mapLoadMessage, webSocketURL, clientCode);
}
);
//!------------ 场景整合处理 ------------!//
/**
* 开始处理场景事件
* @param {Object} sceneOperation 场景
* @example
* {
* destroy: {
* messageType: "scene_destroy", // 标识命名为销毁场景
* name: "water_yy", // 要销毁场景的唯一key
* },
* init: {
* messageType: "scene_init", // 标识命名为加载场景
* name: "air_quality", // 要加载场景的唯一key
* url: "http://10.100.245.102:8089/visualmap/#/MapViews?userName=xuckj4&appType=2D&appName=大气一张图", // 场景应用的预览地址
* }
* }
*/
function handleScene(sceneOperation) {
if (sceneOperation) {
// 销毁场景
if (sceneOperation.destroy) {
destroyScene(sceneOperation.destroy);
}
// 加载场景
if (sceneOperation.init) {
var searchSceneIndex = searchScene(sceneOperation.init);
// 判断url是否存在,不存在,不加载
// 如果场景已经存在,不重复加载
if (sceneOperation.init.url && searchSceneIndex < 0) {
loadScene(sceneOperation.init, searchSceneIndex);
} else {
// 要加载的场景已存在或者场景地址不存在
console.log(sceneIntegration.error.initScene);
}
}
// 待处理任务减1,下个任务将在场景处理完开始
handlingScene.splice(0, 1);
}
}
/**
* 加载场景
* @param sceneObj
* @param searchSceneIndex
*/
function loadScene(sceneObj, searchSceneIndex) {
// 首先获取场景对应所有的配置文件
getConfigOptions(sceneObj, function() {
// 把多级图层转为一级,放到变量curSceneLayers中,为调整面图层顺序做准备
processLayers(sceneOb.layerManager.layers);
// 找出面图层
handlePolygonLayer();
// 合并场景图层和现有图层数据
layers = layers.concat(sceneOb.layerManager.layers);
// 第一次加载场景的时候,把主体的图层信息也放到layers中
if (isAdd) {
layers = layers.concat(allLayers);
isAdd = false;
}
// 把场景信息放到场景集合中
sceneList.push(sceneOb);
// 加载图层
if (sceneOb.layerManager && sceneOb.layerManager.layers) {
// 如果要加载的场景图层为空,直接发消息进入图层加载完的事件
if (sceneOb.layerManager.layers.length === 0) {
publishEvent(topicEvents.layersUpdated);
} else {
// 发消息,让图层列表加载场景图层
// publishEvent('updateLayer', {
// controlLayer: sceneOb.layerManager.layers, // 要加的图层
// type: 'add', // 操作类型为加载
// layers: layers // 加载完后,全部的图层
// });
// TODO 目前需要这种方式才能同时更新图层和图层列表面板,后期可能会有变化
window.postMessage({
controlLayer: sceneOb.layerManager.layers, // 要加的图层
type: "add", // 操作类型为加载
layers: layers, // 加载完后,全部的图层
messageType: "layerChanged",
});
}
}
});
}
/**
* 获取该场景的所有配置文件
* @param sceneObj
* @param callBack
*/
function getConfigOptions(sceneObj, callBack) {
// 要加载场景的预览地址
var appUrl = sceneObj.url;
// 地图类型(2D/3D)
var appType = getUrlParam(mapGoConstants.appType, appUrl);
// 场景的用户名
var userName = getUrlParam(mapGoConstants.userName, appUrl);
// 场景应用标识
var appID = getUrlParam(mapGoConstants.appID, appUrl);
// 获取地址中端口的正则
var hostNamePortReg = /.+:(\d{1,5})/;
// 端口
var hostNamePort = appUrl.match(hostNamePortReg);
// 场景配置文件根目录地址
var appAddressUrl =
hostNamePort[0] +
mapGoConstants.middleUrl +
userName +
"/" +
appType +
"/" +
appID;
// 场景图层列表配置文件完整地址
var layerManager = appAddressUrl + mapGoConstants.configAddress.layerManager;
// 场景时间轴配置文件完整地址
var timeline = appAddressUrl + mapGoConstants.configAddress.timeLine;
// 场景图例配置文件完整地址
var legend = appAddressUrl + mapGoConstants.configAddress.legendManager;
// 场景开发者代码配置文件完整地址
var interactions = appAddressUrl + mapGoConstants.configAddress.interactions;
// 请求配置文件(注意低版本浏览器不支持Promise,请自行引入兼容性代码)
Promise.all([
getData(layerManager),
getData(timeline),
getData(legend),
getData(interactions),
]).then(function(values) {
// 等所有文件请求完执行
// 如果有替换的文本的需求
if (replaceResources && replaceResources.length > 0) {
for (var i = 0; i < replaceResources.length; i++) {
sceneObj.layerManager = replaceStrInJSON(
sceneObj.layerManager || values[0],
new RegExp(replaceResources[i].replaceStr, "g"),
replaceResources[i].replacedStr
);
sceneObj.timeLine = replaceStrInJSON(
sceneObj.timeLine || values[1],
new RegExp(replaceResources[i].replaceStr, "g"),
replaceResources[i].replacedStr
);
sceneObj.legend = replaceStrInJSON(
sceneObj.legend || values[2],
new RegExp(replaceResources[i].replaceStr, "g"),
replaceResources[i].replacedStr
);
if (values[3].replace) {
sceneObj.interactions = (sceneObj.interactions || values[3]).replace(
new RegExp(replaceResources[i].replaceStr, "g"),
replaceResources[i].replacedStr
);
} else {
sceneObj.interactions = sceneObj.interactions || values[3];
}
sceneOb = sceneObj;
}
} else {
sceneOb = sceneObj;
// 使用replaceStrInJSON的目的只要把配置的文本转为对象,保证是否有替换,变量都是一致的
sceneOb.layerManager = replaceStrInJSON(values[0]);
sceneObj.timeLine = replaceStrInJSON(values[1]);
sceneObj.legend = replaceStrInJSON(values[2]);
sceneObj.interactions = values[3]; // 开发者代码就应该是字符串
}
callBack();
});
}
/**
* 订阅图层列表-复数图层加载/更新/销毁完成
* @desc 在场景整合中的作用主要是新场景图层列表加载完成后执行什么
*/
subscribeEvent(topicEvents.layersUpdated, function() {
// 为了和图层列表的事件进行区分
publishEvent(topicEvents.customLayersUpdated);
});
/**
* 图层加载完成或者销毁完成
*/
subscribeEvent(topicEvents.customLayersUpdated, function() {
// 只要走到这里,不管是加载场景还是销毁场景,当前任务就算是处理完了,所以需要去掉当前的任务
handlingScene.splice(0, 1);
// 更新自定义微件(时间轴和图例)
widgetsUpdated();
// 因为销毁场景的时候也会进这里,所有在执行的时候,要先判断sceneList中这个场景存在
if (searchScene(sceneOb) > -1) {
// 如果还有待处理任务,将继续执行下一个任务
if (handlingScene.length > 0) {
// 取出下一个任务(已处理任务的下一个,不是最新的)
var sceneObj = handlingScene[0];
// TODO 可能会出现连续几个任务有问题的情况
// 获取要处理的场景在已有场景的索引(兼可判断场景是否存在)
var searchSceneIndex = searchScene(sceneObj);
// 处理场景任务
handleScene(sceneObj, searchSceneIndex);
}
// 如果当前任务是加载场景,要在图层加载完后执行对应的开发者代码
if (sceneOb.messageType === sceneIntegration.init) {
// 执行开发者代码
playScript(sceneOb);
// 把面图层跳到点图层下面
movePolygonLayer();
}
}
});
/**
* 单独销毁一个场景
* @param {Object} destroySceneInfo 要销毁的场景信息
* @example
* {
* name: 要销毁场景的唯一标识
* }
*/
function destroyScene(destroySceneInfo) {
if (destroySceneInfo.name && destroySceneInfo.name !== "undefined") {
// 获取该场景在场景集合中的索引
var searchSceneIndex = searchScene(destroySceneInfo);
// 场景存在即删除场景
if (searchSceneIndex > -1) {
// 向整个地图发出消息,告诉所有场景要销毁哪个场景了
publishEvent(sceneIntegration.destroy, destroySceneInfo);
// 根据场景集合中的场景图层配置,删除图层集合中的该场景的图层
sceneList[searchSceneIndex].layerManager.layers.forEach(function(layer) {
layers.forEach(function(allLayer, index) {
if (allLayer.name === layer.name) {
layers.splice(index, 1);
}
});
});
// 根据场景信息,减小面图层相关变量
reducePolygonLengthByScene(
sceneList[searchSceneIndex].layerManager.layers
);
// TODO 多场景时,考虑让图层去移除图层
// 删除除初始图层以外的所有图层
onlyRetainInitLayers(initLayers);
// 在所有场景列表中删除该场景
sceneList.splice(searchSceneIndex, 1);
// 都先隐藏时间轴微件、置空图例微件
setCustomWidgetVisible(customWidgetType.timeLine, false);
addLegendToMap(null, [
{
options: [],
defaultVisible: true,
isDefaultOpen: false,
},
]);
}
}
}
/**
* 处理当前场景的面图层
* @description
* 把当前场景的面图层筛选出来(主要为了多场景叠加的时候,新场景的面图层不能把老图层的点线图层盖住)
*/
function handlePolygonLayer() {
if (curSceneLayers.length) {
// geojson面、feature 面 (未考虑image、wmts、wfs)
for (var i = 0; i < curSceneLayers.length; i++) {
if (
(curSceneLayers[i].type === "geojson" ||
curSceneLayers[i].type === "feature") &&
curSceneLayers[i].geometryType === "polygon"
) {
curScensePolygonLayers.push({
layerName: curSceneLayers[i].name,
index: mapPolygonLayerLength + curScensePolygonLayers.length,
});
}
}
mapPolygonLayerLength += curScensePolygonLayers.length;
}
}
/**
* 执行新场景的开发者代码
* @param {Object} sceneObj 场景对象
*/
function playScript(sceneObj) {
// 把开发者代码中默认的代码去掉,只保留用户编写的
var scriptSegment = handleScriptStr(sceneObj);
try {
eval(scriptSegment);
} catch (e) {
console.log(sceneIntegration.error.interactions + e);
}
}
/**
* 处理开发者代码,去掉在这里不需要的代码
* @param {Object} sceneObj 图层对象
* @returns {string} 要执行的开发者代码
*/
function handleScriptStr(sceneObj) {
if (sceneObj.interactions && typeof sceneObj.interactions === "string") {
var code = sceneObj.interactions.split(
mapGoConstants.interactions.oldDefault
)[1];
if (code) {
code = code.slice(0, -50);
} else {
code = sceneObj.interactions
.split(mapGoConstants.interactions.newestDefault)[1]
.slice(0, -4);
code = code.slice(0, code.length - 1);
}
return code;
}
}
/**
* 根据场景信息,降低面图层的相关变量(移除场景时使用)
* @param {object} layerConfig 图层配置
*/
function reducePolygonLengthByScene(layerConfig) {
// 三级变一级,统计当前图层
processLayers(layerConfig);
if (curSceneLayers.length) {
// geojson面、feature 面 (未考虑image、wmts、wfs)
for (var i = 0; i < curSceneLayers.length; i++) {
if (
(curSceneLayers[i].type === "geojson" ||
curSceneLayers[i].type === "feature") &&
curSceneLayers[i].geometryType === "polygon"
) {
mapPolygonLayerLength--;
}
}
}
// 重置
curScensePolygonLayers = [];
curSceneLayers = [];
}
/**
* 把当前场景的面图层跳到所有点位图层的下面
* @description 新场景的面会在旧场景的面的上面
*/
function movePolygonLayer() {
if (curScensePolygonLayers.length) {
for (var i = 0; i < curScensePolygonLayers.length; i++) {
reorderLayer(
curScensePolygonLayers[i].layerName,
curScensePolygonLayers[i].index
);
}
}
// 调整完重置
curScensePolygonLayers = [];
curSceneLayers = [];
}
/**
* 根据场景对象找出在场景列表中的索引
* @param sceneObj 场景对象
* @returns {number}
*/
function searchScene(sceneObj) {
return sceneList.findIndex(function(scene) {
if (scene.name === sceneObj.name) {
return true;
}
});
}
/**
* 自定义微件的更新(图例和时间轴)
*/
function widgetsUpdated() {
// 获取当前最新的场景数据
var sceneNearest = sceneList[sceneList.length - 1];
// 存在,根据数据更新图例和时间轴
if (sceneNearest) {
// 如果厂商在开发者中加的时间轴,需要自行设置时间轴显示
if (sceneNearest.timeLine.length === 0) {
setCustomWidgetVisible(customWidgetType.timeLine, false);
} else {
setCustomWidgetVisible(customWidgetType.timeLine, true);
// 记载时间轴(只加载最新的时间轴)
addTimeLineToMap(null, sceneNearest.timeLine);
}
// 初始图例配置
var legendOptions = {
options: [],
defaultVisible: true,
isDefaultOpen: false,
};
// 遍历所有场景,综合所有场景的要显示的图例配置
sceneList.forEach(function(scene, index) {
// 获取具体的图例配置
var legendConfig = scene.legend;
// 找到默认显示的图层场景
var defaultVisibleTrueConfig = legendConfig.find(function(legend) {
return legend.defaultVisible;
});
// 如果有默认显示的
if (defaultVisibleTrueConfig) {
// 合并图例数据
legendOptions.options = legendOptions.options.concat(
defaultVisibleTrueConfig.options
);
// 如果遍历到最后一个场景
if (index === sceneList.length - 1) {
var mergeLegend = Object.assign(
{},
legendOptions,
defaultVisibleTrueConfig
);
if (mergeLegend.options) {
delete mergeLegend.options;
}
legendOptions = Object.assign({}, legendOptions, mergeLegend);
}
}
});
// 加载图例
addLegendToMap(null, [legendOptions]);
} else {
// 不存在,隐藏时间轴,清空图例
setCustomWidgetVisible(customWidgetType.timeLine, false);
addLegendToMap(null, [
{
options: [],
defaultVisible: true,
isDefaultOpen: false,
},
]);
}
}
/**
* 移除场景的时候只保留初始图层
* @param initLayers 要保留的图层id
*/
// TODO 当支持多场景叠加的时候,需要只去掉销毁场景的图层
function onlyRetainInitLayers(initLayers) {
if (initLayers) {
// 获取当前地图中的所有图层
var curAllLayers = Object.keys(map._layers);
for (var i = 0; i < curAllLayers.length; i++) {
// 如果图层在当前地图中存在
if (initLayers.indexOf(curAllLayers[i]) < 0) {
// 因为不好确定图层类型去使用适应的方法,所有都调一遍
// 删除Echarts图层
removeEchartsLayer(curAllLayers[i], true);
if (getLayer(curAllLayers[i])) {
// 删除HTML图层
removeCustomLayer(curAllLayers[i]);
if (getLayer(curAllLayers[i])) {
// 删除图层
removeLayer(curAllLayers[i]);
}
}
}
}
}
}
/**
* 对JSON字符串或者对应执行字符替换
* @param {(object|string)} config 要替换的字符串或者对象
* @param {(Object|string)} replaceRegExp 正则对象(要替换的规则)
* @param {string} replacedStr 要替换为的字符串
* @return {Object}
*/
function replaceStrInJSON(config, replaceStr, replacedStr) {
var json = config;
if (json) {
try {
if (typeof json !== "string") {
json = JSON.stringify(json);
}
if (json.replace) {
json = JSON.parse(json.replace(replaceStr, replacedStr));
}
} catch (error) {
console.log(sceneIntegration.error.replaceJSONStr + error);
}
}
return json;
}
// 获取初始图层id数组
initLayers = initLayers.concat(Object.keys(map._layers));
//!------------ 相关工具方法 ------------!//
/**
* 获取url中的特定参数的值
* @param {string} name 特定参数(key)
* @param {string} url http地址
* @returns {string|null}
*/
function getUrlParam(name, url) {
var reg = new RegExp(name + "=([^&]*)"); // 构造一个含有目标参数的正则表达式对象
var r = url.substr(1).match(reg); // 匹配目标参数
if (r != null) return unescape(r[1]);
return null; // 返回参数值
}
/**
* 转三级图层列表为一级
* @param {Object} configs 图层配置
* @description 把图层转为以及放到变量curSceneLayers中,为了后面统计面图层和调整面图层顺序做准备
*/
function processLayers(configs) {
if (configs) {
for (var i = 0; i < configs.length; i++) {
if (configs[i].children) {
// 存在子节点,则当前节点为分类,而不是具体图层
processLayers(configs[i].children);
} else {
curSceneLayers.push(configs[i]);
}
}
}
}
/**
* 加载数据
* @param {string} url 数据地址
* @returns {Promise}
*/
function getData(url) {
// 如果需要对地址进行操作,请处理后在传入该函数
return new Promise(function(resolve, reject) {
fetch(encodeURI(url))
.then(function(response) {
return response.text();
})
.then(function(data) {
resolve(data);
})
.catch(function(e) {
resolve({});
});
});
}
//!------------ 模拟大屏消息 ------------!//
// 向地图中添加两个按钮并模拟大屏消息加载场景
createButton(20, 100, "加载水,销毁固定源", function() {
sendMessage({
type: "runInteractive",
data: [
{
code: "thsGis",
shareCode: "thsGis",
defaultValue: "",
runtimeValue: JSON.stringify([
{
messageType: "scene_init",
name: "water",
url:
"http://121.46.19.2:20724/mapgo/#/MapViews?userName=template&appType=2D&appID=z1zb1vbokmhajncu",
},
{
messageType: "scene_destroy",
name: "stationary-source",
},
]),
},
],
});
});
createButton(20, 300, "加载固定源,销毁水", function() {
sendMessage({
type: "runInteractive",
data: [
{
code: "thsGis",
shareCode: "thsGis",
defaultValue: "",
runtimeValue: JSON.stringify([
{
messageType: "scene_init",
name: "stationary-source",
url:
"http://121.46.19.2:20724/mapgo/#/MapViews?userName=template&appType=2D&appID=z1zbremkmhak2t9",
},
{
messageType: "scene_destroy",
name: "water",
},
]),
},
],
});
});
/**
* 向页面中添加个按钮(绝对定位)
* @param {number} top 距上面的像素
* @param {number} left 距左侧的像素
* @param {string} text 显示文本
* @param {Function} clickCallback 点击的回调
*/
function createButton(top, left, text, clickCallback) {
// 向body中添加按钮
var btn = document.createElement("button");
btn.style.width = "150px";
btn.style.height = "40px";
btn.style.position = "absolute";
btn.style.top = top + "px";
btn.style.left = left + "px";
btn.innerText = text;
document.querySelector("body").appendChild(btn);
if (clickCallback) {
btn.onclick = clickCallback;
}
}
# 场景
//订阅销毁场景的事件
var subscribeHandler = null;
subscribeEvent(
"scene_destroy",
function(sceneObj) {
//注意一定要根据场景名称判断
if (sceneObj.name === "water") {
console.log("水场景接收到了场景销毁事件");
// 需要处理的操作逻辑,销毁地图监听事件
// if (handler && handler.remove instanceof Function) {
// handler.remove();
// }
//移除通过代码添加的图层
// 取消订阅的销毁场景事件
if (subscribeHandler) {
unsubscribe(subscribeHandler);
}
// 取消连接
if (thsMapSocket) {
// water要和建立WebSocket方法的第二个参数的key一致
thsMapSocket.removeListenMessage("water");
}
}
},
function(event) {
subscribeHandler = event;
}
);
# 部分变量说明
# sceneList
已加载/正在加载的的场景的配置信息集合
# 示例
[
{
name: "air_quality", // 场景应用的唯一标识(要求场景之间,该名称唯一)
messageType: "scene_init", // 大屏消息中标识操作的类型(scene_init:加载场景)
url:
"http://10.100.245.102:8089/visualmap/#/MapViews?userName=xuckj4&appType=2D&appName=大气一张图", // 场景的预览地址(如果没有平台支持或者已经把场景应用配置单独拿出来,请自行改为能指向对应文件的路径并修改相关加载逻辑)
layerManager: {}, // 场景应用的图层配置
legend: [], // 场景应用的图例配置
timeLine: [], // 场景应用的时间轴配置
interactions: "if(window.addEventListener){window.addEventListene...", // 场景应用的完整开发者代码
},
];
# replaceResources
针对场景配置文件进行整体文字替换的配置
# 描述
主要是在服务迁移、文件路径变更时使用,例如:整体替换 IP、端口、图标路径等。
# 针对范围
针对场景配置的所有文本信息。(有需要请自行变更代码逻辑)
# 示例
[
{
replaceStr: "10.100.245.102:8089", // 要替换的字符
replacedStr: "10.100.245.220:8089", // 修改后的字符
},
];
# handlingScene
待处理的加载/销毁场景命令的集合
# 描述
- 在场景整合中都是处理完一个命令再处理另一个命令的,为了保证所有操作都能处理并且防止相关逻辑冲突
- 现在加载场景和销毁的命令是合一的
# 示例
{
destroy: {
messageType: "scene_destroy", // 标识命名为销毁场景
name: "water_yy", // 要销毁场景的唯一key
},
init: {
messageType: "scene_init", // 标识命名为加载场景
name: "air_quality", // 要加载场景的唯一key
url: "http://10.100.245.102:8089/visualmap/#/MapViews?userName=xuckj4&appType=2D&appName=大气一张图", // 场景应用的预览地址
}
}
# sceneOb
保存当前获取到的并 JSON 化的场景应用配置数据
# 示例
内容和 sceneList 的子级对象结构相同