import { IPSSysMap, IPSSysMapItem } from '@ibiz/dynamic-model-api'; import { init, registerMap } from 'echarts'; import { MapControlInterface, Util } from "ibiz-core"; import { MDControlBase } from "./md-control-base"; import { AppMapService } from '../ctrl-service'; import { Subscription } from 'rxjs'; /** * 地图部件基类 * * @export * @class MapControlBase * @extends {MDControlBase} */ export class MapControlBase extends MDControlBase implements MapControlInterface { /** * 地图的模型对象 * * @type {*} * @memberof MapControlBase */ public declare controlInstance: IPSSysMap; /** * 地图对象 * * @type {*} * @memberof MapControlBase */ public map: any; /** * 获取地址需求AMap插件对象 * * @type {*} * @memberof MapControlBase */ public geocoder: any; /** * 当前 window * * @type {*} * @memberof MapControlBase */ public win: any; /** * 地图信息缓存 * * @memberof MapControlBase */ public addressCache: Map<string, any> = new Map(); /** * 获取选中数据 * * @returns {any[]} * @memberof GridControlBase */ public getSelection(): any[] { return this.selections; } /** * 地图数据 * * @type {*} * @memberof MapControlBase */ public items: Array<any> = []; /** * 地图数据项模型 * * @type {} * @memberof MapControlBase */ public mapItems: any = {}; /** * 显示图例 * * @type {*} * @memberof MapControlBase */ public showLegends: any[] = []; /** * 显示的最大值 * * @type {number} * @memberof MapControlBase */ public valueMax: number = 0; /** * 省份区域数据 * * @memberof MapControlBase */ public areaData: any = []; /** * 区域样式图数据 * * @memberof MapControlBase */ public regionData: any = []; /** * @description 地图部件事件 * @type {(Subscription | undefined)} * @memberof MapControlBase */ public mapControlEvent: Subscription | undefined; /** * 省份区域值集合 * * @memberof MapControlBase */ public getAreaValueList() { return [ { name: '南海诸岛', value: this.calculateAreaValue('南海') }, { name: '北京', value: this.calculateAreaValue('北京') }, { name: '天津', value: this.calculateAreaValue('天津') }, { name: '上海', value: this.calculateAreaValue('上海') }, { name: '重庆', value: this.calculateAreaValue('重庆') }, { name: '河北', value: this.calculateAreaValue('河北') }, { name: '河南', value: this.calculateAreaValue('河南') }, { name: '云南', value: this.calculateAreaValue('云南') }, { name: '辽宁', value: this.calculateAreaValue('辽宁') }, { name: '黑龙江', value: this.calculateAreaValue('黑龙江') }, { name: '湖南', value: this.calculateAreaValue('湖南') }, { name: '安徽', value: this.calculateAreaValue('安徽') }, { name: '山东', value: this.calculateAreaValue('山东') }, { name: '新疆', value: this.calculateAreaValue('新疆') }, { name: '江苏', value: this.calculateAreaValue('江苏') }, { name: '浙江', value: this.calculateAreaValue('浙江') }, { name: '江西', value: this.calculateAreaValue('江西') }, { name: '湖北', value: this.calculateAreaValue('湖北') }, { name: '广西', value: this.calculateAreaValue('广西') }, { name: '甘肃', value: this.calculateAreaValue('甘肃') }, { name: '山西', value: this.calculateAreaValue('山西') }, { name: '内蒙古', value: this.calculateAreaValue('内蒙古') }, { name: '陕西', value: this.calculateAreaValue('陕西') }, { name: '吉林', value: this.calculateAreaValue('吉林') }, { name: '福建', value: this.calculateAreaValue('福建') }, { name: '贵州', value: this.calculateAreaValue('贵州') }, { name: '广东', value: this.calculateAreaValue('广东') }, { name: '青海', value: this.calculateAreaValue('青海') }, { name: '西藏', value: this.calculateAreaValue('西藏') }, { name: '四川', value: this.calculateAreaValue('四川') }, { name: '宁夏', value: this.calculateAreaValue('宁夏') }, { name: '海南', value: this.calculateAreaValue('海南') }, { name: '台湾', value: this.calculateAreaValue('台湾') }, { name: '香港', value: this.calculateAreaValue('香港') }, { name: '澳门', value: this.calculateAreaValue('澳门') }, ] }; /** * 初始化配置 * * @type {} * @memberof MapControlBase */ public initOptions: any = { title: { text: '', left: 'center', top: 20, }, tooltip: { trigger: 'item', }, legend: { orient: 'vertical', x: 'left', y: 'center', data: [], }, geo: { map: 'china', zoom: 1.2, label: { show: true, }, itemStyle: { areaColor: '#e0ffff', }, emphasis: { itemStyle: { areaColor: '#F3B329', }, }, }, visualMap: [ { min: 0, max: 1000, left: 'left', seriesIndex: 0, top: 'bottom', text: ['高', '低'], inRange: { color: ['#e0ffff', '#006edd'], }, show: true, }, ], series: [ { name: '信息量', type: 'map', geoIndex: 0, data: [], tooltip: { formatter: "<span style='margin-right: 20px'>{b}</span><b>{c}</b>", }, } ] } /** * 默认排序方向 * * @readonly * @memberof MapControlBase */ public minorSortDir: any = ''; /** * 默认排序应用实体属性 * * @readonly * @memberof MapControlBase */ public minorSortPSDEF: any = ''; /** * 监听静态参数变化 * * @param {*} newVal * @param {*} oldVal * @memberof MapControlBase */ public onStaticPropsChange(newVal: any, oldVal: any) { this.isSelectFirstDefault = newVal.isSelectFirstDefault ? true : false; super.onStaticPropsChange(newVal, oldVal); } /** * 部件模型数据初始化实例 * * @memberof MapControlBase */ public async ctrlModelInit(args?: any) { await super.ctrlModelInit(); if (!(this.Environment && this.Environment.isPreviewMode)) { this.service = new AppMapService(this.controlInstance, this.context); await this.service.loaded(this.controlInstance); this.initMapModel(); this.registerMap(); } } /** * @description 注册地图 * @memberof MapControlBase */ registerMap() { const geoJson = require(`@/assets/json/map/china.json`); registerMap('china', geoJson); } /** * 初始化 * * @memberof MapControlBase */ public ctrlInit(args?: any) { super.ctrlInit(); // 绑定this this.transformData = this.transformData.bind(this); this.refresh = this.refresh.bind(this); this.win = window as any; // 初始化默认值 if (this.viewState) { this.mapControlEvent = this.viewState.subscribe(({ tag, action, data }: { tag: string, action: string, data: any }) => { if (!Object.is(this.name, tag)) { return; } if (Object.is(action, 'load')) { this.load(data); } if (Object.is(action, 'updateSize')) { this.updateSize(); } }); } } /** * 部件挂载完毕 * * @protected * @memberof MapControlBase */ public ctrlMounted(): void { super.ctrlMounted(); let _this: any = this; let map: any = (this.$refs.map as any); if (map) { this.map = init(map); } if (this.map) { this.map.setOption(this.initOptions); // 如果是导航视图默认选中第一项 this.map.on('selectchanged', 'series', function (params: any) { if (params.fromActionPayload?.custom) { const select: any[] = [params.fromActionPayload.custom]; _this.onClick(select); } }); // 监听地图触发的点击事件 this.map.on('click', 'series', function (params: any) { if (Object.is(params.seriesType, 'scatter') && params.data?.value) { const select: any[] = [params.data.value[3]]; _this.onClick(select); } else if ((Object.is(params.seriesType, 'custom') || Object.is(params.seriesType, 'lines')) && params.data?.coords) { const selects: any[] = []; params.data.coords.forEach((coord: any) => { selects.push(coord[3]) }) _this.onClick(selects); } }); // 图例改变过滤地图数据 this.map.on('legendselectchanged', function (params: any) { if (params.name) { _this.showLegends.forEach((showLegend: any) => { if (Object.is(showLegend.name, params.name)) { showLegend.show = !showLegend.show; } }) // 重新设置地图数据 _this.valueMax = 0; const data = _this.getAreaValueList(); _this.map.setOption({ visualMap: { max: _this.valueMax > 0 ? _this.valueMax : 1000, }, series: { data: data, } }) } }); } let amap: any = this.win.AMap; amap.plugin(["AMap.Geocoder"], () => { this.geocoder = new amap.Geocoder({ extensions: "all", }) }) } /** * 刷新 * * @param {*} [args] 额外参数 * @memberof MapControlBase */ public refresh(args: any = {}) { this.load(args); } /** * 更新大小 * * @memberof MapControlBase */ public updateSize() { if (this.map) { this.map.resize(); } } /** * 地图数据加载 * * @param {*} [data={}] 额外参数 * @returns {void} * @memberof MapControlBase */ public async load(data: any = {}): Promise<void> { if (!this.fetchAction) { this.$throw(this.$t('app.map.notconfig.fetchaction'), 'load'); return; } this.items = []; const parentData: any = {}; this.ctrlEvent({ controlname: this.name, action: "beforeload", data: parentData }); Object.assign(data, parentData); let tempViewParams: any = parentData.viewparams ? parentData.viewparams : {}; if (this.viewparams) { Object.assign(tempViewParams, Util.deepCopy(this.viewparams)); } for (let item in this.mapItems) { let sort = ''; if (this.mapItems[item].sort) { sort += this.mapItems[item].sort + ','; } if (sort) { sort = sort + 'desc'; Object.assign(tempViewParams, { sort: sort }); } } Object.assign(data, { viewparams: tempViewParams }); let tempContext: any = Util.deepCopy(this.context); if (!(await this.handleCtrlEvents('onbeforeload', { action: this.fetchAction, navContext: tempContext, navParam: data }))) { return; } this.onControlRequset('load', tempContext, data); try { const response = await this.service.search(this.fetchAction, this.context, data) if (!response || response.status !== 200) { if (!(await this.handleCtrlEvents('onloaderror', { action: this.fetchAction, navParam: data, data: response?.data }))) { return; } this.$throw(response, 'load'); } this.items = response.data; if (!(await this.handleCtrlEvents('onloadsuccess', { action: this.fetchAction, navParam: data, data: this.items }))) { return; } this.ctrlEvent({ controlname: this.name, action: 'load', data: this.items, }); this.calculateAreaData().then(() => { this.setAreaData(); this.handleMapOptions(); this.setOptions(); // 处理地图所有数据需要一定时间,所以将关闭遮罩放置在地图绘制完成之后 this.onControlResponse('load', response); }); } catch (error: any) { this.onControlResponse('load', error); if (!(await this.handleCtrlEvents('onloaderror', { action: this.fetchAction, navParam: data, data: error?.data }))) { return; } this.$throw(error, 'load'); } } /** * 设置配置 * * @memberof MapControlBase */ public setOptions() { if (!this.map) { return; } const options = Util.deepCopy(this.initOptions); this.map.setOption(options); // 如果是导航视图默认选中第一项 if (this.isSelectFirstDefault) { const select: any = this.items.find((item: any) => Object.is(item.itemType, this.showLegends[0]?.itemType)); this.map.dispatchAction({ type: 'select', seriesId: this.showLegends[0]?.itemType, dataIndex: 0, custom: select, }); }; this.updateSize(); } /** * 配置整合 * * @return {*} * @memberof MapControlBase */ public handleMapOptions() { let series: Array<any> = this.initOptions.series; if (!series || series.length == 0) { return; } series.forEach((serie: any) => { const seriesData: Array<any> = [] this.items.forEach((item: any) => { if (Object.is(item.itemType, serie.itemType)) { seriesData.push(item); } }) this.handleSeriesOptions(seriesData, serie); }); Object.assign(this.initOptions.series, series); } /** * 整合序列数据 * * @param {*} seriesData 序列数据 * @param {*} serie 序列 * @memberof MapControlBase */ public handleSeriesOptions(seriesData: Array<any>, serie: any) { serie.id = serie.itemType; if (Object.is(serie.type, 'scatter')) { // 点样式数据处理 serie.data = []; seriesData.forEach((data: any) => { const tempItem = [ parseFloat(data.longitude), parseFloat(data.latitude), data.content, data ]; const _data = { name: data.title, value: tempItem, }; serie.data.push(_data); }); } else if (Object.is(serie.type, 'lines') || Object.is(serie.type, 'custom')) { const groupDatas: Array<any> = []; // 获取对应分组的数据集合 const getGroupItems = (groupName: any) => { const items: Array<any> = []; if (groupName) { seriesData.forEach((data: any) => { if (Object.is(data.group, groupName)) { items.push(data); } }); }; return items; } // 分组 if (this.mapItems[serie.itemType].group) { seriesData.forEach((data: any) => { if (data.group) { const group = groupDatas.find((groupData: any) => Object.is(groupData.group, data.group)); if (!group) { groupDatas.push({ group: data.group, items: getGroupItems(data.group), }); }; }; }); } else { groupDatas.push({ group: serie.name, items: seriesData, }); } if (Object.is(serie.type, 'custom')) { // 区域图数据处理 this.regionData = []; } groupDatas.forEach((groupData: any) => { if (this.mapItems[serie.itemType].sort) { groupData.items.sort((a: any, b: any) => { const x: any = a.sort; const y: any = b.sort; return x > y ? -1 : x < y ? 1 : 0; }) }; const coords: Array<any> = []; groupData.items.forEach((item: any) => { const coord: any[] = [ parseFloat(item.longitude), parseFloat(item.latitude), item.content, item ]; coords.push(coord); }); serie.data.push({ name: groupData.group, coords: coords, }); }); if (Object.is(serie.type, 'custom')) { // 区域图数据处理 this.regionData = [...serie.data]; } } } /** * 初始化地图参数 * * @memberof MapControlBase */ public initMapModel() { const mapItems: IPSSysMapItem[] | null = this.controlInstance.getPSSysMapItems(); this.showLegends = []; if (mapItems) { mapItems.forEach((item: IPSSysMapItem, index: number) => { this.showLegends.push( { name: item.name, itemType: item.itemType?.toLowerCase(), show: true, } ); Object.assign(this.mapItems, { [item.itemType?.toLowerCase()]: { bkcolor: item.bKColor, color: item.color, content: item.getContentPSAppDEField()?.codeName.toLowerCase(), latitude: item.getLatitudePSAppDEField()?.codeName.toLowerCase(), longitude: item.getLongitudePSAppDEField()?.codeName.toLowerCase(), text: item.getTextPSAppDEField()?.codeName.toLowerCase(), tips: item.getTipsPSAppDEField()?.codeName.toLowerCase(), group: item.getGroupPSAppDEField()?.codeName.toLowerCase(), sort: item.getOrderValuePSAppDEField()?.codeName.toLowerCase(), code: index } }); this.initOptions.legend.data.push(item.name); this.initOptions.visualMap.push( { type: 'piecewise', left: 'left', top: 'bottom', splitNumber: 1, seriesIndex: index + 1, pieces: [{ label: item.name, min: index * 10, max: (index + 1) * 10, color: item.color, backgroundColor: item.bKColor, borderColor: item.borderColor, borderWidth: item.borderWidth }], show: false } ); if (Object.is('POINT', item.itemStyle)) { this.initOptions.series.push( { name: item.name, type: 'scatter', coordinateSystem: 'geo', itemType: item.itemType?.toLowerCase(), color: item.color, selectedMode: 'single', select: { itemStyle: { borderColor: item.color, opacity: 1, borderWidth: 6, }, }, itemStyle: { opacity: 0.7, }, geoIndex: 0, symbolSize: 16, label: { show: true, position: 'right', formatter: '{b}', }, emphasis: {}, encode: { value: 2, }, tooltip: {}, data: [], } ); } else if (Object.is('LINE', item.itemStyle)) { this.initOptions.series.push( { name: item.name, type: 'lines', geoIndex: 0, itemType: item.itemType?.toLowerCase(), coordinateSystem: 'geo', polyline: true, tooltip: { show: true, formatter: (arg: any) => this.renderTooltip(arg), }, lineStyle: { color: item.color, opacity: 1, width: 3, }, data: [], } ) } else if (Object.is('REGION', item.itemStyle)) { this.initOptions.series.push( { name: item.name, type: 'custom', geoIndex: 0, itemType: item.itemType?.toLowerCase(), coordinateSystem: 'geo', renderItem: (params: any, api: any) => this.renderRegion(params, api), selectedMode: 'single', itemStyle: { color: item.color, opacity: 0.5, }, tooltip: { formatter: (arg: any) => this.renderTooltip(arg), }, data: [], } ) } }); } } /** * 绘制悬浮提示 * * @param arg */ public renderTooltip(arg: any) { const curData: any = arg.data; const items: any[] = curData.coords; let content: any = ''; if (Object.is("lines", arg.seriesType)) { content = items[0][2] + '-' + items[items.length - 1][2]; } else { let total: number = 0; items.forEach((item: any) => { total += item[2]; }); content = total; } const interval: any = ' '; const tooltip = arg.name + '<br />' + arg.marker + curData.name + interval + content; return tooltip; } /** * 绘制区域图 * * @param params 参数 * @param api 方法集合 * @returns */ public renderRegion(params: any, api: any) { const children: any[] = []; const color = api.visual('color'); this.regionData.forEach((data: any) => { let points: any[] = []; data.coords.forEach((value: any) => { points.push(api.coord(value)); }); const child: any = { type: 'polygon', shape: { points: points, }, style: api.style({ fill: color, }), select: { style: { opacity: 1, fill: color, }, }, } children.push(child); }); return { type: 'group', children: children, }; } /** * 调用服务,根据经纬度获取地址信息 * * @param {*} lng 经度 * @param {*} lat 纬度 * @memberof MapControlBase */ public async getAddress(lng: any, lat: any) { return new Promise((resolve, reject) => { let address: any = null; if (this.addressCache.get(lng + '-' + lat)) { address = this.addressCache.get(lng + '-' + lat); resolve(address); } this.geocoder.getAddress([lng, lat], (status: any, result: any) => { if (status === 'complete' && result.info === 'OK') { if (result && result.regeocode) { address = result.regeocode.addressComponent; this.addressCache.set(lng + '-' + lat, address); } } resolve(address); }) }); } /** * 计算省份区域数据 * * @memberof MapControlBase */ public async calculateAreaData() { this.areaData = []; if (this.items.length > 0) { for (let item of this.items) { const address: any = await this.getAddress(item.longitude, item.latitude); if (address) { const provinceName = address.province; this.areaData.push({ name: provinceName, itemType: item.itemType, data: item, }) } } } } /** * 计算省份区域数据值(项类容属性) * * @param name 省份区域名 * @returns 省份区域值 * @memberof MapControlBase */ public calculateAreaValue(name: string) { const areaData: any = []; this.areaData.forEach((item: any) => { const showLegend = this.showLegends.find((_showLegend: any) => Object.is(_showLegend.itemType, item.itemType)); if (item.name.indexOf(name) > -1 && showLegend?.show) { areaData.push(item.data); } }) let value: number = 0; areaData.forEach((item: any) => { if (Number.isFinite(Number(item.content))) { value += item.content; } else { value += 1; } }) this.valueMax = value > this.valueMax ? value : this.valueMax; return value; } /** * 设置地图数据(初始化) * * @memberof MapControlBase */ public setAreaData() { let series: Array<any> = this.initOptions.series; if (!series || series.length == 0) { return; } this.valueMax = 0; this.initOptions.title.text = this.controlInstance?.logicName; this.initOptions.series[0].data = this.getAreaValueList(); this.initOptions.visualMap[0].max = this.valueMax > 0 ? this.valueMax : 1000; } /** * 地图点击事件 * * @param $event 选中数据 * @memberof MapControlBase */ public onClick($event: any[]) { this.selections = $event; if ($event && (Object.keys($event).length > 0)) { this.$emit("ctrl-event", { controlname: this.controlInstance.name, action: "selectionchange", data: this.selections }); } } /** * @description 部件销毁 * @memberof MapControlBase */ public ctrlDestroyed(){ super.ctrlDestroyed() if(this.mapControlEvent){ this.mapControlEvent.unsubscribe(); } } }