rt-sim-training-client/src/jmapNew/map.js
2020-10-13 09:36:48 +08:00

707 lines
28 KiB
JavaScript

import * as zrUtil from 'zrender/src/core/util';
import zrender from 'zrender';
import localStore from 'storejs';
import Painter from './painter';
import Options from './options';
import MouseController from './mouseController';
import KeyboardController from './keyboardController';
import deviceState from './constant/deviceState'; // 初始化默认状态
import deviceType from './constant/deviceType';
import { selectLineCode } from './config/deviceStyle';
import { deviceFactory, createBoundingRect, calculateDCenter } from './utils/parser';
import { deepAssign } from '@/utils/index';
import store from '@/store/index_APP_TARGET';
import Vue from 'vue';
const renderer = 'canvas';
const devicePixelRatio = 1;
class Jlmap {
constructor(opts) {
// 回调事件
this.methods = opts.methods;
// 鼠标事件
this.events = { __Pan: 'pan', __Zoom: 'zoom', Selected: 'selected', Contextmenu: 'contextmenu', DataZoom: 'dataZoom', Keyboard: 'keyboard'};
// 线路参数
this.lineCode = '';
// 大屏判断
this.screenFlag = false;
// 皮肤风格
this.style = {};
// 设备数据
this.mapDevice = {};
// 默认状态
this.defaultStateDict = this.loadDefaultState();
this.initMapInstance(opts);
// 显示集中站
this.stationCode = '';
this.isUpdateShowTrainList = false;
}
// 初始化属性有鼠标事件 缩放等
initMapInstance(opts) {
const width = opts.dom.clientWidth;
const height = opts.dom.clientHeight;
this.zoomOnMouseWheel = opts.options.zoomOnMouseWheel;
this.previewOrMapDraw = opts.showConfig.previewOrMapDraw;
this.$zr = zrender.init(opts.dom, deepAssign({ renderer, devicePixelRatio, width, height }, opts.config));
this.$zr.dom.setAttribute('tabIndex', -1);
this.$options = new Options(deepAssign({ scaleRate: 1, offsetX: 0, offsetY: 0 }, opts.options || {}), (dataZoom) => { this.$mouseController.trigger(this.events.DataZoom, dataZoom); }); // 缩放
this.$painter = new Painter(this);
this.$painter.updateZrSize({width: this.$zr.getWidth(), height: this.$zr.getHeight()});
this.$zr.dom.style.backgroundColor = this.style.backgroundColor || '#000';
this.optionsHandler = this.setOptions.bind(this);
this.$mouseController = new MouseController(this);
this.$mouseController.enable();
this.$mouseController.on(this.events.__Pan, this.optionsHandler);
this.$mouseController.on(this.events.__Zoom, this.optionsHandler);
this.$keyboardController = new KeyboardController(this);
this.$keyboardController.enable();
this.showConfig = opts.showConfig;
}
loadStyle(lineCode) {
return selectLineCode(lineCode);
}
loadDefaultState() { // 加载默认状态
const defaultStateDict = {};
zrUtil.each(Object.keys(deviceState), (type) => {
defaultStateDict[type] = {};
zrUtil.each(Object.keys(deviceState[type] || {}), (state) => {
defaultStateDict[type][state] = deviceState[type][state]; // 新版地图直接使用判断结果
}, this);
}, this);
return defaultStateDict;
}
setMap(map, mapDevice, logicData) {
// 保存皮肤类型
if (map.skinVO) {
this.lineCode = map.skinVO.code;
this.$options.scaleRate = map.scaling || 1;
this.$options.offsetX = map.origin ? map.origin.x : 0;
this.$options.offsetY = map.origin ? map.origin.y : 0;
}
// 更新视图大小
this.$painter.updateTransform({ scaleRate: this.$options.scaleRate, offsetX: this.$options.offsetX, offsetY: this.$options.offsetY });
// 解析后的数据
this.mapDevice = mapDevice;
// 进路数据
this.logicData = logicData;
// 加载对应皮肤
this.style = this.loadStyle(this.lineCode);
this.$zr.dom.style.backgroundColor = this.style.backgroundColor || '#000';
// 数据加载完成 回调
if (this.methods.dataLoaded instanceof Function) { this.methods.dataLoaded(this.mapDevice); }
// 初次渲染视图
this.$painter.repaint(this.mapDevice);
// 视图加载完成 回调
if (this.methods.viewLoaded instanceof Function) { this.methods.viewLoaded(this.mapDevice); }
}
setMapDevice(mapDevice) {
this.mapDevice = mapDevice;
}
setDefaultState() {
const list = [];
Object.values(this.mapDevice).forEach(elem => {
const code = elem.code;
const type = elem._type;
// 列车不需要设置默认状态
type != deviceType.Train && list.push(Object.assign({ code, _type: type }, this.defaultStateDict[type]));
});
this.update(list);
if (this.methods.stateLoaded instanceof Function) { this.methods.stateLoaded(list); }
}
setOptions(opts) {
const options = this.pullBack(opts);
if (this.screenFlag) {
this.$painter.updateScreen(options);
} else {
this.$options.update(options);
this.$painter.updateTransform(this.$options);
}
if (this.$options.disabled == true) {
this.$mouseController.disable();
} else {
opts['zoomOnMouseWheel'] = this.zoomOnMouseWheel;
opts['previewOrMapDraw'] = this.previewOrMapDraw;
this.$mouseController.enable(opts);
}
if (this.methods.optionsUpdate instanceof Function) { this.methods.optionsUpdate(this.$options); }
}
switchScreen(data, num, sum) {
const sumHalf = sum <= 2 ? sum : sum / 2;
const dx = data.width / sumHalf;
const dy = data.height / sumHalf;
const row = num <= sumHalf ? 0 : 1;
const opts = {
dx: dx * (row == 0 ? num - 1 : num - sumHalf - 1),
dy: dy * row
};
this.$painter.updateScreenNum(opts);
}
setCenter(deviceCode) {
const arr = Object.keys(this.mapDevice);
if (arr.length != 0) {
const device = this.mapDevice[deviceCode];
if (device && device.instance) {
var rect = createBoundingRect(device.instance);
var dcenter = calculateDCenter(rect, { width: this.$zr.getWidth(), height: this.$zr.getHeight() });
this.setOptions(dcenter);
}
} else {
setTimeout(() => {
this.setCenter(deviceCode);
}, 60);
}
}
setRevoverBigScreen() {
this.screenFlag = false;
this.$painter.updateTransform({ scaleRate: this.$options.scaleRate, offsetX: this.$options.offsetX, offsetY: this.$options.offsetY });
}
setRecover(opts) {
this.screenFlag = false;
this.$painter.updateTransform({ scaleRate: opts.scaleRate, offsetX: opts.offsetX, offsetY: opts.offsetY });
}
setUpdateScreen(opts) {
this.setRecover({ scaleRate: 1, offsetX: 0, offsetY: 0 });
this.screenFlag = true;
const screenList = [];
const rectList = [];
let rect = '';
for (const i in this.mapDevice) {
const element = this.mapDevice[i];
if (element.instance && (element._type == 'Section' || element._type == 'Station' || element._type == 'StationStand')) {
if (!rect) {
rect = element.instance.getBoundingRect().clone();
} else {
rect.union(element.instance.getBoundingRect().clone());
}
}
}
if (!opts.offsetList) {
opts.offsetList = [];
}
const screenSplit = opts.list.length ? opts.list : Vue.prototype.$theme.loadPropConvert(this.lineCode).screenSplit;
const screenDifference = Vue.prototype.$theme.loadPropConvert(this.lineCode).screenDifference || 160;
const splitList = JSON.parse(JSON.stringify(screenSplit));
const num = screenSplit.length + 1;
const offsetY = (opts.height - 100) / num; // 高度差
const maxWidth = rect.width;
splitList.push(maxWidth);
const scaleX = Math.floor((((opts.width - 200) * num) / rect.width) * 100) / 100;
const scaleY = Math.floor(((opts.height - 100) / (rect.height * num)) * 100) / 100;
const scale = Math.min(scaleX, scaleY);
for (let i = 0; i < splitList.length; i++) {
let offsetX = '';
if (i == 0) {
offsetX = -(opts.width - splitList[0] * scale) / 2;
} else {
const dx = (opts.width - (splitList[i] - splitList[i - 1]) * scale) / 2; // 居中计算偏移值
offsetX = splitList[i - 1] * scale - dx;
}
const offset = opts.offsetList[i] || 0;
const param = { scaleRateX: scale, scaleRateY: scale, offsetX: offsetX, offsetY: -screenDifference - (offsetY * i) - offset };
screenList.push(param);
const rect = {x: 0, y: 0, width: Number(splitList[i]) + 5, height: opts.height};
rectList.push(rect);
}
this.$painter.updateTransform1(screenList, rectList);
}
setLevelVisible(list) {
this.$painter.setLevelVisible(list);
}
render(list) {
(list || []).forEach(elem => {
const code = elem.code;
const type = elem._type;
if (type == 'OverAp') {
const overlapRoute = store.state.map.overlapData[code];
const model = this.mapDevice[overlapRoute.pathList[0].sectionList[0]];
if (overlapRoute.pathList[0].right) {
elem['points'] = { x: model.points[0].x, y: model.points[0].y };
} else {
elem['points'] = { x: model.points[model.points.length - 1].x, y: model.points[model.points.length - 1].y };
}
}
// const oDevice = this.mapDevice[code] || deviceFactory(type, elem, this.showConfig);
const oDevice = deviceFactory(type, elem, this.showConfig);
// const nDevice = deepAssign(oDevice || {}, elem);
// if (this.mapDevice[code]) {
// nDevice.instance = this.mapDevice[code].instance; // 保持相同 instance
// }
// this.$painter.delete(oDevice);
// delete this.mapDevice[code];
// if (!elem._dispose) {
// this.mapDevice[code] = nDevice;
// this.$painter.add(nDevice); // 重新赋值instance
// }
if (this.mapDevice[code]) {
this.$painter.delete(this.mapDevice[code]);
delete this.mapDevice[code];
}
if (!elem._dispose) {
this.mapDevice[code] = deepAssign(this.mapDevice[code] || {}, oDevice);
this.$painter.add(this.mapDevice[code]);
}
});
if (this.methods.viewUpdate instanceof Function) { this.methods.viewUpdate(list); }
this.updateShowMode(list, this.showConfig.showMode);
this.updateShowStation(list, this.stationCode);
}
// 中间处理
hookHandle(oDevice, elem) {
// 老数据 新数据
const code = elem.code;
const type = elem._type;
// 如果是延时计时,需要保存计数值到全局
if (type === deviceType.StationCounter) {
let val = '' + elem.val;
if (val === '0' || !elem.val) {
val = elem.val = localStore.get(code) || '0';
}
localStore(code, val);
}
for (var prop in elem) {
if (oDevice[prop] != elem[prop]) {
deepAssign(oDevice, elem); // 更新状态
return true;
}
}
return false;
}
// 后处理
postHandle(list) {
list.forEach(elem => {
const code = elem.code;
const type = elem._type;
if (type == deviceType.Switch) {
const item = this.mapDevice[code];
if (item) {
const sectionA = this.mapDevice[item.sectionACode];
const sectionB = this.mapDevice[item.sectionBCode];
const sectionC = this.mapDevice[item.sectionCCode];
if (sectionA && sectionB && sectionC) {
item['cutOff'] = sectionA.cutOff;
}
}
this.$painter.update(item);
}
if (type == deviceType.Section) {
const item = this.mapDevice[code];
if (item) {
const swch = this.mapDevice[item.relSwitchCode];
if (swch) {
const sectionA = this.mapDevice[swch.sectionACode];
const sectionB = this.mapDevice[swch.sectionBCode];
const sectionC = this.mapDevice[swch.sectionCCode];
if (sectionA && sectionB && sectionC) {
swch['cutOff'] = sectionA.cutOff;
}
this.$painter.update(swch);
}
}
}
});
}
setUpdateMapDevice(list) {
store.dispatch('map/updateMapDevice', list);
(list || []).forEach(elem => {
const code = elem.code;
const type = elem._type;
const oDevice = this.mapDevice[code] || deviceFactory(type, elem);
this.hookHandle(oDevice, elem);
});
}
// 模式选择 -> 更新
updateShowMode(list, showMode) {
this.showConfig.showMode = showMode;
(list || []).forEach(elem => {
const code = elem.code;
const type = elem._type;
const oDevice = this.mapDevice[code] || deviceFactory(type, elem, this.showConfig);
oDevice.showMode = showMode;
this.$painter.updateShowMode(oDevice);
});
this.$painter.$transformHandle.revisibleAll();
}
getShowConfig() {
return this.showConfig;
}
updateShowStation(list, stationCode) {
this.stationCode = stationCode;
(list || []).forEach(elem => {
const code = elem.code;
const type = elem._type;
const oDevice = this.mapDevice[code] || deviceFactory(type, elem, this.showConfig);
if (oDevice._type === deviceType.Station) {
this.showStationHandleStation(oDevice, stationCode);
} else if (oDevice._type === deviceType.Section) {
this.$painter.updateShowStation(oDevice, stationCode);
this.showStationHandleSection(oDevice, stationCode);
} else if (oDevice._type === deviceType.TrainWindow) {
// 不处理车次窗 勿删
} else if (oDevice._type === deviceType.Psd) {
this.showStationHandlePsd(oDevice, stationCode);
} else {
this.$painter.updateShowStation(oDevice, stationCode);
}
});
this.$painter.$transformHandle.revisibleAll();
}
showStationHandlePsd(oDevice, stationCode) {
const standDevice = this.mapDevice[oDevice.standCode];
if (standDevice && standDevice.deviceStationCode === stationCode || !stationCode) {
this.$painter.updateSpecialShowStation(oDevice, true);
} else {
this.$painter.updateSpecialShowStation(oDevice, false);
}
}
showStationHandleSection(oDevice, stationCode) {
const trainWinDevice = this.mapDevice[oDevice.trainWindowCode];
if (oDevice.stationCode === stationCode || !stationCode) {
this.$painter.updateSpecialShowStation(trainWinDevice, true);
} else {
this.$painter.updateSpecialShowStation(trainWinDevice, false);
}
}
showStationHandleStation (oDevice, stationCode) {
if ((oDevice.centralized && oDevice.code === stationCode) || !stationCode) {
this.$painter.updateSpecialShowStation(oDevice, true);
oDevice.chargeStationCodeList.forEach(staCode => {
const staDevice = this.mapDevice[staCode];
this.$painter.updateSpecialShowStation(staDevice, true);
});
} else if (oDevice.centralized && oDevice.code !== stationCode) {
this.$painter.updateSpecialShowStation(oDevice, false);
oDevice.chargeStationCodeList.forEach(staCode => {
const staDevice = this.mapDevice[staCode];
this.$painter.updateSpecialShowStation(staDevice, false);
});
}
}
updatePrdType(val, list) {
this.showConfig.prdType = val;
(list || []).forEach(item => {
this.hookHandle(item, this.showConfig);
this.$painter.update(item);
});
}
update(list) {
this.setUpdateMapDevice(list || []); // 增加一个 前数据 处理 为了在区段中 获取全部的 道岔信息
const controlTransferList = [];
const signalDeviceList = [];
(list || []).forEach((elem, index) => {
const code = elem.code;
const type = elem._type;
if (elem.deviceType === 'ROUTE' && this.logicData.routeData) { // 处理进路数据状态
store.dispatch('map/updateRouteState', elem);
const route = this.logicData.routeData[code];
if (route.automaticRouteCode) {
const automaticRoute = this.mapDevice[route.automaticRouteCode];
const automaticRouteStatus = {fleetMode: elem.fleetMode};
if (this.hookHandle(automaticRoute, automaticRouteStatus)) {
this.$painter.update(automaticRoute);
}
}
const signalDevice = this.mapDevice[route.startSignalCode];
const index = signalDeviceList.indexOf(signalDevice);
if (index === -1) {
signalDeviceList.push(signalDevice);
}
} else if (elem.deviceType == 'OVERLAP') {
const overlapRoute = this.mapDevice[elem.code];
const model = this.mapDevice[overlapRoute.pathList[0].sectionList[0]];
if (overlapRoute.pathList[0].right) {
overlapRoute['points'] = { x: model.points[0].x, y: model.points[0].y };
} else {
overlapRoute['points'] = { x: model.points[model.points.length - 1].x, y: model.points[model.points.length - 1].y };
}
overlapRoute['offsetRight'] = overlapRoute.pathList[0].right;
overlapRoute['sectionName'] = model.name;
this.$painter.update(overlapRoute);
} else if (elem.deviceType === 'CYCLE') {
store.dispatch('map/updateAutoReentryState', elem);
const autoReentryData = this.logicData.autoReentryData[code];
const cycleButton = this.mapDevice[autoReentryData.cycleButtonCode];
const cycleButtonStatus = {setUp: elem.setUp};
if (cycleButton && this.hookHandle(cycleButton, cycleButtonStatus)) {
this.$painter.update(cycleButton);
}
} else if (elem.deviceType === 'STATION') {
// 如果是控制权转移消息
if (elem.hasOwnProperty('applicantId')) {
controlTransferList.push(elem);
}
if (!elem.applicantId) {
const oDevice = this.mapDevice[code] || deviceFactory(type, elem, this.showConfig);
const guideLock = this.mapDevice[oDevice.guideLockCode];
const guideLockStatus = {totalGuideLock: elem.totalGuideLock};
if (guideLock && this.hookHandle(guideLock, guideLockStatus)) {
this.$painter.update(guideLock);
}
if (elem.dispose) {
this.$painter.delete(oDevice);
} else {
this.$painter.update(oDevice);
}
}
} else {
if (elem.deviceType === 'TRAIN') {
this.isUpdateShowTrainList = true;
store.dispatch('map/updateActiveTrainList', elem);
// store.dispatch('map/updateTrainState', elem);
} else if (elem.deviceType === 'STAND') {
store.dispatch('map/updateStationStand', elem);
const psdDevice = this.mapDevice[this.mapDevice[elem.code].psdCode];
if (psdDevice) {
psdDevice.fault = elem.fault;
this.$painter.update(psdDevice);
}
} else if (elem.deviceType === 'DIRECTION_ROD' && elem.uniqueCode) {
store.state.map.map.directionRodList.forEach(item => {
if (elem.uniqueCode === `${item.startStationCode}-${item.endStationCode}-${item.right}`) {
this.$painter.update(elem);
}
});
}
const oDevice = this.mapDevice[code] || deviceFactory(type, elem, this.showConfig);
if (elem.dispose) {
this.$painter.delete(oDevice);
} else {
// if (this.hookHandle(oDevice, elem)) {
this.$painter.update(oDevice);
// }
}
}
});
if (controlTransferList.length > 0) {
store.dispatch('map/setControlTransfer', controlTransferList);
}
if (this.isUpdateShowTrainList) {
store.dispatch('map/setActiveTrainList');
this.isUpdateShowTrainList = false;
}
this.handleRouteSignalStatus(signalDeviceList);
// 状态后处理
this.postHandle(list || []);
if (this.methods.stateUpdate instanceof Function) { this.methods.stateUpdate(list); }
}
handleRouteSignalStatus(signalDeviceList) {
const routeStartSignalData = store.state.map.routeStartSignalData;
signalDeviceList.forEach((item, index)=> {
const status = {};
if (routeStartSignalData[item.code] && routeStartSignalData[item.code].length) {
routeStartSignalData[item.code].forEach((elem, index)=> {
if (index) {
status.atsControl = status.atsControl && elem.atsControl;
status.fleetMode = status.fleetMode || elem.fleetMode;
status.ciControl = status.ciControl || elem.ciControl;
} else {
status.atsControl = elem.atsControl;
status.fleetMode = elem.fleetMode;
status.ciControl = elem.ciControl;
}
status.canSetCi = status.arc || elem.arc;
});
}
if (item && this.hookHandle(item, status)) {
this.$painter.update(item);
}
});
}
pullBack(payload) {
if (payload.type === 'zoom') {
const zrWidth = this.$zr.getWidth();
const zrHeight = this.$zr.getHeight();
const originX = payload.originX || zrWidth / 2;
const originY = payload.originY || zrHeight / 2;
const x = (this.$options.offsetX + originX) / this.$options.scaleRate;
const y = (this.$options.offsetY + originY) / this.$options.scaleRate;
const newScaleRate = this.$options.getScaleRate(payload.scale);
const dx = originX - (x * newScaleRate - this.$options.offsetX);
const dy = originY - (y * newScaleRate - this.$options.offsetY);
payload.dx = dx;
payload.dy = dy;
}
return payload || {};
}
getZr() {
return this.$zr;
}
getEvents() {
return this.events;
}
getDeviceByCode(code) {
return this.mapDevice[code];
}
getShapeTipPoint(opts) {
const device = this.mapDevice[opts.code];
if (device) {
return this.$painter.getShapeTipPoint(device.instance, opts);
}
}
resize(opt) {
this.$zr.resize(opt);
this.$painter.updateZrSize(opt);
}
refresh() {
this.$painter.refresh();
}
clearTrainView() {
this.$painter.clearLevel(deviceType.Train);
zrUtil.each(Object.values(this.mapDevice), device => {
if (device._type == deviceType.Train) {
device.instance = null;
}
});
}
clear() {
this.lineCode = '';
this.style = {};
this.mapDevice = {};
this.$painter.clear();
}
dispose() {
this.off(this.events.Pan, this.optionsHandler);
this.off(this.events.Zoom, this.optionsHandler);
this.clear();
this.$mouseController.dispose();
this.$keyboardController.dispose();
this.$zr && zrender.dispose(this.$zr);
this.$painter.dispose();
}
on(eventname, cb, context) {
const idx = Object.values(this.events).indexOf(eventname);
if (idx >= 0) {
switch (eventname) {
case this.events.Selected:
this.$mouseController.on(this.events.Selected, cb, context);
break;
case this.events.Contextmenu:
this.$mouseController.on(this.events.Contextmenu, cb, context);
break;
case this.events.DataZoom:
this.$mouseController.on(this.events.DataZoom, cb, context);
break;
case this.events.Keyboard:
this.$keyboardController.on(this.events.Keyboard, cb, context);
break;
case this.events.__Pan:
this.$mouseController.on(this.events.__Pan, this.optionsHandler);
break;
case this.events.__Zoom:
this.$mouseController.on(this.events.__Zoom, this.optionsHandler);
break;
}
}
}
off(eventname, cb) {
const idx = Object.values(this.events).indexOf(eventname);
if (idx >= 0) {
switch (eventname) {
case this.events.Selected:
this.$mouseController.off(this.events.Selected, cb);
break;
case this.events.Contextmenu:
this.$mouseController.off(this.events.Contextmenu, cb);
break;
case this.events.DataZoom:
this.$mouseController.off(this.events.DataZoom, cb);
break;
case this.events.Keyboard:
this.$keyboardController.off(this.events.keyboard, cb);
break;
case this.events.__Pan:
this.$mouseController.off(this.events.__Pan, cb);
break;
case this.events.__Zoom:
this.$mouseController.off(this.events.__Zoom, cb);
break;
}
}
}
renderCheckBox(model) {
const type = model._type;
const code = model.code;
const oDevice = this.mapDevice[code] || deviceFactory(type, model);
const nDevice = deviceFactory(type, Object.assign(oDevice.model || {}, model));
delete this.mapDevice[code];
this.$painter.delete(oDevice);
if (!model._dispose) {
this.mapDevice[code] = nDevice;
this.$painter.add(nDevice);
}
}
deleteCheckBox(code) {
const oDevice = this.mapDevice[code];
if (oDevice) {
delete this.mapDevice[code];
this.$painter.delete(oDevice);
}
}
}
export default Jlmap;