import { Widget } from "./Widget";
import { Http } from "../Http";
import { FeatureCollection } from "../models/FeatureCollection";
import { MosaicData } from "../models/MosaicData";
import { Geometry } from "../models/Geometry";
import { Feature } from "../models/Feature";
import { Image } from "../models/Image";
import { Guid } from "@gvol-org/geovis-brain-core";
import { DrawingTools } from "./DrawingTools";
import * as turf from '@turf/turf';
import { Subject } from "rxjs";
import Compare from "mapbox-gl-compare";
import { ui } from "./ui";
import mapboxgl from "mapbox-gl";
import { AddLayer } from "../models/AddLayer";
import { MapUtil } from "@gvol-org/geovis-brain-core";
import { Mvt } from "../models/Mvt";
import { BatchProcessing } from "../batchProcessingsdk/BatchProcessing";
/**
* @class ui.Map
* @since ui
*/
export class Map extends Widget {
addLayerParams: any = [];//保存addLayer方法的参数
addUIParams: any = [];//保存addUI方法的参数
removeUIParams: any = [];//保存removeUI方法的参数
removeLayerParams: any = [];//保存removeLayer方法的参数
flytoParams: any = [];//保存flyto方法的参数
additionLayer: any;//保存卷帘函数额外需要添加的图层
controlVisibilityParams: any = [];
setCenterParams: object;
setZoomParam: number;
map: any;//mapbox对象
id: string;
onClick: Function;
layerList: any = [];//addLayer的图层列表
scaleDic: any = [156543.03, 78271.52, 39135.76, 19567.88, 9783.94, 4891.97, 2445.98, 1222.99, 611.50, 305.75, 152.87, 76.44, 38.22, 19.11, 9.55, 4.78, 2.39, 1.19, 0.60, 0.30];
mapUI: HTMLElement;
drawTools: DrawingTools;
toolBtnLocat: any; //设置地图上的按钮的位置
featuresLayerInfo: Array<any> = [];
loadTileCount: number = 0;
requestTileTotal: number = 0;
loadTilePercent: number = 0;
showTileLoadProgress: boolean = false;
/**
* @hideconstructor
* @param {object} [center] 地图默认中心点
* @param {Function} [onClick] 地图点击事件
* @param {object} [style] 地图样式
* @return ui.Map
*/
constructor(
center?: object,
onClick?: Function,
style?: object
) {
let mapContainer = document.createElement("div");
mapContainer.style.flex = '1';
mapContainer.style.height = '100%';
mapContainer.style.width = '100%';
mapContainer.style.position = 'relative';
mapContainer.style.display = 'block';
mapContainer.style.minHeight = '200px';
mapContainer.style.minWidth = '200px';
let mapUI = document.createElement("div");
mapUI.classList.add("map-ui-div");
mapContainer.appendChild(mapUI);
super(mapContainer, style);
if (!(this instanceof Map)) {
return new Map(center, onClick, style);
}
this.mapUI = mapUI;
if (center) {
this.setCenterParams = center;
}
if (onClick) {
this.onClick = onClick;
}
this.id = Guid.newGuid();
Http.generateComponentSubject$.next({
name: 'Map',
component: this,
data: {
id: this.id,
map: this.map
}
});
this.toolBtnLocat = {
baseMapLocat: "bottom-right",
layerListLocat: "top-right",
navigationCtrlLocat: "bottom",
FullscreenCtrlLocat: "bottom-right"
}
}
setSubject(sub: any) {
this.stateChanged$ = sub;
}
public stateChanged$ = new Subject()
//用于监听setptions的调用
setOptionsSubject(sub: any){
this.changeBaseMap$ = sub;
}
public changeBaseMap$ = new Subject();
/**
* 在地图上添加图层,图层类型可以是Image、Geometry、Feature、FeatureCollection,返回图层唯一的ID
* @param {Image|Geometry|Feature|FeatureCollection} image 作为图层添加的图像数据
* @param {Object} [visParams] 可选参数,。属性:style(应用到图层的样式),palette(用于图层的调色板), crop(WKT类型的Geometry对象或gpkg文件路径),min, max, gamma, brightness
* @param {String} [name] 可选参数,给予图层的名称。
* @param {Boolean} [visible] 可选参数,该图层是否可见,默认为true
* @return {String} 添加图层的Id
*/
public addLayer(image: Image | Geometry | Feature | FeatureCollection | MosaicData | Mvt,
visParams?: Object,
name?: string,
visible?: boolean): string {
let addLayerParam: any = {};
addLayerParam.id = Guid.newGuid();
addLayerParam.image = image;
addLayerParam.visParams = visParams;
addLayerParam.name = name;
addLayerParam.visible = visible;
this.addLayerParams.push(addLayerParam);
if (typeof process === 'undefined') {
if (this.map && this.map.style._loaded) {
this._addLayer(addLayerParam);
}
}
else {
this._addLayer(addLayerParam);
}
return addLayerParam.id;
}
/**
* 在地图上添加自定义ui
* @param {Widget} ui 自定义ui
*/
public addUI(ui: Widget) {
this.addUIParams.push(ui);
if (this.map && this.map.style._loaded) {
this._addUI(ui);
}
}
/**
* 移除地图上的自定义ui
* @param {Widget} ui 自定义ui
*/
public removeUI(ui: Widget) {
this.removeUIParams.push(ui);
}
/**
* 根据提供的ID移除图层
* @param {String} Id 待删除图层的ID
*/
public removeLayer(id: string) {
this.removeLayerParams.push(id);
}
/**
* 清空已添加的所有图层
* @param {String} [param] 影像的url
*/
public clear() {
if (this.layerList) {
this.layerList?.forEach((data: any) => {
data?.onLoadCompleted(() => {
data?.mapboxLayresId.forEach((lid: any) => {
this.map.removeLayer(lid);
});
data?.mapboxSourcesId.forEach((sid: any) => {
this.map.removeSource(sid);
});
});
});
}
BatchProcessing.image.clearMap();
}
/**
* 图层过滤显示
* @param {String} id 图层的id
* @param {String} classId 过滤的图层类别
*/
public setFilter(id: string, classId: string) {
MapUtil.setFilter(this, id, classId);
}
/**
* 相机飞入到指定图层位置
* @param {String | Array<number>} param 图层的Id
*/
public flyto(param: string | Array<number>) {
this.flytoParams.push(param);
if (this.map && this.map.style._loaded) {
this._flyto(param);
}
}
//代替flyto,使用value接口
/**
* 相机飞入到指定图层位置
* @param {Object} object 图层对象
* @param {number} [zoom] 地图显示缩放级别(0-24)
*/
public async centerObject(object: Object, zoom?: number) {
await MapUtil.centerObject(this, object, zoom);
}
/**
* 返回当前地图视图的近似像素比例,单位为米
* @returns number
*/
public getScale() {
if (this.map && this.map.style._loaded) {
let zoomLevel = Http.zoomLevel == -1?Math.round(this.map.getZoom()) + 1:Http.zoomLevel;
if (zoomLevel < this.scaleDic.length) {
return this.scaleDic[zoomLevel];
}
else {
return null;
}
}
else {
return '地图尚未加载,无法获取地图分辨率';
}
}
/**
* 修改地图底图
* @param {String} mapTypeId 可以是 "SATELLITE"、"ROADMAP" 或 "TERRAIN" 之一
*/
public setOptions(mapTypeId:string): any {
this.changeBaseMap$.next(mapTypeId);
return null;
}
/**
* 返回地图视图的当前缩放级别
* @returns number
*/
public getZoom() {
if (this.map && this.map.style._loaded) {
return this.map.getZoom();
}
else {
if (this.setZoomParam) {
return this.setZoomParam;
}
else {
return 3;//默认级别
}
}
}
/**
* 返回地图视图的地理中心
* @returns Geometry.Point
*/
public getCenter() {
if (this.map && this.map.style._loaded) {
const center = this.map.getCenter();
return { lng: center.lng, lat: center.lat };
}
else {
if (this.setCenterParams && !isNaN(this.setCenterParams['lon']) && !isNaN(this.setCenterParams['lat'])) {
return { lng: this.setCenterParams['lon'], lat: this.setCenterParams['lat'] };
}
else {
return { lng: 116.39880632, lat: 38.90487972 };//默认中心点
}
}
}
/**
* 返回地图视图的地理边界
* @returns GeoJSONGeometry|Array<Number>|String
*/
public getBounds() {
if (this.map && this.map.style._loaded) {
return this.map.getBounds();
}
else {
return '地图尚未加载,无法获取边界';
}
}
/**
* 设置地图中心显示
* @param {Number} lon 地图显示中心点的经度
* @param {Number} lat 地图显示中心点的经度
* @param {Number} [zoom] 可选参数,地图显示缩放级别(0-24)
*/
public setCenter(lon: number, lat: number, zoom?: number) {
this.setCenterParams = { lon: lon, lat: lat, zoom: zoom };
if (this.map && this.map.style._loaded) {
this.map.setCenter([lon, lat]);
if (zoom) {
this.map.setZoom(zoom);
}
}
}
/**
* 设置地图显示缩放级别
* @param {Number} zoom 地图显示缩放级别(0-24)
*/
public setZoom(zoom: number) {
this.setZoomParam = zoom;
if (this.map && this.map.style._loaded) {
this.map.setZoom(zoom);
}
}
/**
* 放大地图
*/
public zoomIn() {
if (this.map && this.map.style._loaded) {
return this.map.zoomIn();
}
else {
return '地图尚未加载,无法放大地图';
}
}
/**
* 缩小地图
*/
public zoomOut() {
if (this.map && this.map.style._loaded) {
return this.map.zoomOut();
}
else {
return '地图尚未加载,无法缩小地图';
}
}
/**
* 返回绘制工具
*/
public drawingTools() {
//返回绘制工具
if (!this.drawTools) {
this.drawTools = new DrawingTools();
}
return this.drawTools;
}
/**
* 设置地图控件显示隐藏
* @param {boolean} all 是否显示所有控件。 true表示显示;false表示隐藏。
* @param {boolean} layerList 是否显示图层控制控件。true表示显示;false表示隐藏。
* @param {boolean} [zoomControl] 是否显示缩放控件。true表示显示;false表示隐藏。
* @param {boolean} [scaleControl] 是否显示比例尺控件。 true表示显示;false表示隐藏。
* @param {boolean} [mapTypeControl] 是否显示底图切换控件。true表示显示;false表示隐藏。
* @param {boolean} [fullscreenControl] 是否显示全屏控件。true表示显示;false表示隐藏。
* @param {boolean} [drawingToolsControl] 是否显示绘图控件。true表示显示;false表示隐藏。
*/
public setControlVisibility(all?: boolean, layerList?: boolean, zoomControl?: boolean, scaleControl?: boolean, mapTypeControl?: boolean, fullscreenControl?: boolean, drawingToolsControl?: boolean) {
//设置控件显示
let controlVisibility = {};
controlVisibility['all'] = all;
controlVisibility['layerList'] = layerList;
controlVisibility['zoomControl'] = zoomControl;
controlVisibility['scaleControl'] = scaleControl;
controlVisibility['mapTypeControl'] = mapTypeControl;
controlVisibility['fullscreenControl'] = fullscreenControl;
controlVisibility['drawingToolsControl'] = drawingToolsControl;
this.controlVisibilityParams.push(controlVisibility);
}
getType(): string {
return 'Map';
}
private _addLayer(options: any) {
return MapUtil.addLayer(options, this);
}
private _flyto(param: string | Array<number>) {
MapUtil.flyto(this, param);
}
private _addUI(ui: Widget) {
if (this.mapUI) {
if (!Http.isRunAddUICode) {
Http.runDataSubject$.next({
type: 'ui',
content: Http.codeContent,
});
Http.isRunAddUICode = true;
}
if (ui.el) {
let style = ui.style();
// console.log('mapStyle:', style);
if (!style.get('position')) {
style.set('position', 'absolute');
if (!style.get('right') && !style.get('bottom') && !style.get('left') && !style.get('top')) {
style.set('left', '50%');
style.set('top', '10px');
}
}
style.set('z-index', '99');
}
this.mapUI.appendChild(ui.el as HTMLElement);
if (ui['cht']) {
(ui.el as HTMLElement).style.width = '300px';
ui['cht']?.resize();
}
}
}
//运行结果调用
public runResult(data: any) {
if (data) {
switch (data.type) {
case "Geojson":
{
const { geojsonData, sourceId, layerId, style, render_sld } = data.params;
MapUtil.addSourceByGeojson(this.map, geojsonData, sourceId, layerId, style, render_sld);//绘制图层
data.bbox = turf.bbox(geojsonData);
}
break;
case "Pbf":
{
const { serviceInfo, sourceId, layerId, visParams } = data.params;
MapUtil.addSourceByPbf(this.map, serviceInfo, sourceId, layerId, visParams);//绘制图层
}
break;
case "Url":
{
const {
serviceInfo,
sourceId,
layerId,
urlTiles,
visible,
visParams
} = data.params;
MapUtil.addSourceByUrl(
this.map,
serviceInfo,
sourceId,
layerId,
urlTiles,
visible,
visParams
);//绘制图层
}
break;
case "Mvt":
{
const { serviceInfo, sourceId, layerId, visParams } = data.params;
MapUtil.addSourceByMvt(this.map, serviceInfo, sourceId, layerId, visParams);//绘制图层
}
break;
}
if (data.bbox) {
this.map.fitBounds(data.bbox);//视角飞入
}
}
}
//卷帘加的底图,为了适配屏幕布局变化进行resize
public compareMapList: any[] = [];
/**
* 卷帘
* @param {Image} left 左侧显示的Image实例
* @param {Image} right 右侧显示的Image实例
* @param {any[]} additionLayer 额外在左右两侧地图上都需要加的图层
*/
public CompareImage(left: Image, right?: Image, additionLayer?: any[]) {
this.compareMapList = [];
let mapboxStyle;
let mapStatus;
if (typeof process === 'undefined') {
//先清除原有地图
// ui.root.clear();
const link = document.createElement("link"); // 创建 link 元素
link.rel = "stylesheet"; // 设置 link 的 rel 属性为 stylesheet
link.href = "https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-compare/v0.4.0/mapbox-gl-compare.css"; // 设置 link 的 href 属性为 CSS 文件的 URL
link.type = "text/css";
(ui.root.el as any).appendChild(link); // 将 link 元素添加到指定元素的子节点中
let mapStyle = {
position: "absolute",
top: 0,
bottom: 0,
width: "100%",
height: "100%",
"z-index": 1
}
let beforeDiv = ui.Panel(undefined, undefined, mapStyle);
(beforeDiv.el as any).id = "before";
let afterDiv = ui.Panel(undefined, undefined, mapStyle);
(afterDiv.el as any).id = "after";
let compareStyle = {
height: "100%",
width: "100%",
position: "absolute",
left: "0px",
top: "0px",
"z-index": 1
}
let compareDiv = ui.Panel([beforeDiv, afterDiv], ui.Panel.Layout.flow('horizontal'), compareStyle);
(compareDiv.el as any).id = "comparison-container";
ui.root.add(compareDiv);
// 创建卷帘
mapboxgl.accessToken = 'pk.eyJ1IjoiYXJhcGF5IiwiYSI6ImNsNXEyYXJhMDB2MWkzaWw4dWgxcWptOHYifQ.UYSWGUj_ufnOu-7KACtl4w';
let center = this.getCenter();
let zoom = this.getZoom();
mapStatus = {
center: [center.lng, center.lat],
zoom: zoom
}
mapboxStyle = this.map.getStyle();
}
let beforeMap = new AddLayer("before", mapStatus, mapboxStyle);
let afterMap = new AddLayer("after", mapStatus, mapboxStyle);
this.compareMapList.push(beforeMap.map);
this.compareMapList.push(afterMap.map);
let leftLayer = undefined;
let rightLayer = undefined;
if (left) {
let leftLayerid = beforeMap.addLayer(left);
if (typeof process === 'undefined') {
leftLayer = beforeMap.layerList.find((v: any) => { return v.id == leftLayerid });
// 加上额外的图层
if (additionLayer && additionLayer.length > 0) {
leftLayer.onLoadCompleted(() => {
additionLayer.forEach(la => {
let tempLayerid = beforeMap.addLayer(la, { style: { polygonFillOpacity: 0 } });
let tempLayer = beforeMap.layerList.find((v: any) => { return v.id == tempLayerid });
//每次都把新加的图层置于顶层
for (let x = 0; x < tempLayer.mapboxLayresId.length; x++) {
beforeMap.map.moveLayer(tempLayer.mapboxLayresId[x], undefined);
}
})
})
}
}
}
// MapSdk.compareMapSubject$.next([beforeMap, afterMap]);
if (right) {
let rightLayerid = afterMap.addLayer(right);
if (typeof process === 'undefined') {
rightLayer = afterMap.layerList.find((v: any) => { return v.id == rightLayerid });
// 加上额外的图层
if (additionLayer && additionLayer.length > 0) {
rightLayer.onLoadCompleted(() => {
additionLayer.forEach(la => {
let tempLayerid = afterMap.addLayer(la, { style: { polygonFillOpacity: 0 } });
let tempLayer = afterMap.layerList.find((v: any) => { return v.id == tempLayerid });
//每次都把新加的图层置于顶层
for (let x = 0; x < tempLayer.mapboxLayresId.length; x++) {
afterMap.map.moveLayer(tempLayer.mapboxLayresId[x], undefined);
}
})
})
}
}
}
if (leftLayer && !rightLayer) {
beforeMap.flyto(leftLayer.id);
} else if (rightLayer) {
afterMap.flyto(rightLayer.id);
}
const container = '#comparison-container';
if (typeof process === 'undefined') {
const map = new Compare(beforeMap.map, afterMap.map, container, {});
}
// ///////////////////////////////////////////////////////用ui.Map来实现
// //先清除原有地图
// ui.root.clear();
// const link = document.createElement("link"); // 创建 link 元素
// link.rel = "stylesheet"; // 设置 link 的 rel 属性为 stylesheet
// link.href = "https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-compare/v0.4.0/mapbox-gl-compare.css"; // 设置 link 的 href 属性为 CSS 文件的 URL
// link.type = "text/css";
// (ui.root.el as any).appendChild(link); // 将 link 元素添加到指定元素的子节点中
// let center = this.getCenter();
// let zoom = this.getZoom();
// let mapStyle = {
// position: "absolute",
// top: 0,
// bottom: 0,
// width: "100%",
// height: "100%",
// "z-index": 1
// }
// let beforeDiv = ui.Map({ lon: center.lng, lat: center.lat, zoom: zoom }, undefined, mapStyle);
// beforeDiv.setControlVisibility(true, true, false, false, true, false, false);
// //卷帘左侧底图切换按钮要显示在最左
// beforeDiv.toolBtnLocat.baseMapLocat = 'bottom-left';
// //卷帘左侧图层按钮要显示在最右
// beforeDiv.toolBtnLocat.layerListLocat = "top-left";
// let afterDiv = ui.Map({ lon: center.lng, lat: center.lat, zoom: zoom }, undefined, mapStyle);
// afterDiv.setControlVisibility(true, true, false, false, true, false, false);
// let compareStyle = {
// height: "100%",
// width: "100%",
// position: "absolute",
// left: "0px",
// top: "0px",
// "z-index": 1
// }
// let compareDiv = ui.Panel([beforeDiv, afterDiv], ui.Panel.Layout.flow('horizontal'), compareStyle);
// (compareDiv.el as any).id = "comparison-container";
// ui.root.add(compareDiv);
// // 创建卷帘
// let leftLayerId: string = undefined;
// let rightLayerId: string = undefined;
// if (left) {
// leftLayerId = beforeDiv.addLayer(left);
// // 加上额外的图层
// if (additionLayer && additionLayer.length > 0) {
// beforeDiv.addAdditionLayer(beforeDiv, leftLayerId, additionLayer);
// }
// }
// if (right) {
// rightLayerId = afterDiv.addLayer(right);
// // 加上额外的图层
// if (additionLayer && additionLayer.length > 0) {
// afterDiv.addAdditionLayer(afterDiv, rightLayerId, additionLayer);
// }
// }
// if (leftLayerId && !rightLayerId) {
// beforeDiv.flyto(leftLayerId);
// } else if (rightLayerId) {
// afterDiv.flyto(rightLayerId);
// }
// const container = '#comparison-container';
// Compare.prototype._setPosition = function(x: any){
// // console.log("调用了重写的comapare函数")
// x = Math.min(x, this._horizontal
// ? this._bounds.height
// : this._bounds.width);
// var pos = this._horizontal
// ? 'translate(0, ' + x + 'px)'
// : 'translate(' + x + 'px, 0)';
// this._controlContainer.style.transform = pos;
// this._controlContainer.style.WebkitTransform = pos;
// var clipA = this._horizontal
// ? 'rect(0, 999em, ' + x + 'px, 0)'
// : 'rect(0, ' + x + 'px, ' + this._bounds.height + 'px, 0)';
// var clipB = this._horizontal
// ? 'rect(' + x + 'px, 999em, ' + this._bounds.height + 'px,0)'
// : 'rect(0, 999em, ' + this._bounds.height + 'px,' + x + 'px)';
// this._mapA.getContainer().style.clip = clipA;
// this._mapB.getContainer().style.clip = clipB;
// if(this.options.divA){
// this.options.divA.el.style.clip = clipA;
// }
// if(this.options.divB){
// this.options.divB.el.style.clip = clipB;
// }
// this.currentPosition = x;
// }
// const map = new Compare(beforeDiv.map, afterDiv.map, container, {divA: beforeDiv, divB: afterDiv});
// /////////////////////////////////////////////////////////////////////
}
//在指定id的图层加载完之后再加载附加图层
public addAdditionLayer(object: any, id: string, additionLayer: any[]) {
// console.log("objjjjjjjjjjjjjjj", this.layerList[0])
if (this.map && this.map.style._loaded) {
let res = this.layerList.find((v: any) => v.id == id);
// console.log("add addtionlayer....res", res, id)
if (res && res.layer) {
res.layer.onLoadCompleted(() => {
additionLayer.forEach(la => {
let tempLayerId = this.addLayer(la);
let tempLayer = this.layerList.find((v: any) => { return v.id == tempLayerId });
if (tempLayer) {
//每次都把新加的图层置于顶层
for (let x = 0; x < tempLayer.layer.mapboxLayresId.length; x++) {
this.map.moveLayer(tempLayer.layer.mapboxLayresId[x], undefined);
}
}
})
});
}
}
}
}