AI对话SDk
可以AI对话工具栏对话中点击导出工程模板 点击下载工程代码 , 在src\chat\sdk.ts 查看全部代码知识库。
import vjmap, { Map } from "vjmap";
import vjcommon from "vjcommon";
export class VjChatSDK {
map: Map;
context: Record<string, any>;
constructor(map: Map, context: Record<string, any>) {
this.map = map;
this.context = context;
}
/**
* 通过前缀从服务端获取用户自定义数据的键值
* @param {string} prefix 键名前缀
* @returns {Promise<string[]>} 返回获取到的键值数组
* @example
* // 从服务端通过前缀获取用户自定义数据的键值,前缀为test
* let keys = await this.getCustomDataKeysByPrefixServer("test");
* console.log(keys);
*/
async getCustomDataKeysByPrefixServer(prefix) {
let svc = this.map.getService();
let res = await svc.getCustomDataKeysByPrefix(prefix);
return res?.keys || [];
}
/**
* 创建点标记数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {string} options.text 要显示的文字,参数可选,默认为"index ${props.index}"
* @param {number} options.minZoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxZoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isDrawLinePoint 绘制点时包括线上所有点, 参数可选,默认为false
* @param {string} options.color 标记颜色,参数可选,默认为'#3FB1CE'
* @param {number[]} options.offset 偏离XY像素值,如[0,0],负数表示向左和向上
* @param {number} options.rotation 标记旋转角度,顺时针旋转,默认0
* @param {string} options.anchor 标记锚点位置,可选值:center、top、bottom、left、right、top-left、top-right、bottom-left、bottom-right,默认center
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {string} options.animationType 动画类型:MAP_ANIMATION_NONE(无动画)、MAP_ANIMATION_BOUNCE(弹跳)、MAP_ANIMATION_DROP(坠落)
* @param {string} options.rotationAlignment 旋转对齐:auto(自动)、map(相对地图)、viewport(相对视口),默认auto
* @param {string} options.pitchAlignment 倾斜对齐:auto(自动)、map(相对地图)、viewport(相对视口),默认auto
* @param {number} options.scale 缩放比例,默认1,对应高度41px宽度27px
* @param {number} options.scaleMaxZoom 能缩放的最大级别(0-24),小于此级别时自动缩放
* @param {number} options.height 标高值,单位CAD图制图单位
* @param {boolean} options.removeWhenNoInMapView 是否在视图范围内自动显隐
* @param {number} options.removeWhenNoInMapViewPadding 视图范围内自动显隐的padding像素值,默认500
* @param {number} options.clickTolerance 点击像素容差,默认继承地图的clickTolerance
* @param {string} options.customImage 自定义图片地址/base64/svg内容
* @param {number} options.customImageWidth 自定义图片宽度
* @param {number} options.customImageHeight 自定义图片高度
* @param {string} options.customHtml 自定义Html元素的JS代码
* @param {string} options.popupHtml 信息窗口HTML内容
* @param {[number,number]} options.popupOffset 信息窗口偏离XY像素值
* @param {string} options.popupAnchor 信息窗口锚点位置,同anchor参数
* @param {boolean} options.closeButton 是否显示关闭按钮
* @param {boolean} options.closeOnClick 是否在点击地图时关闭弹出窗口
* @param {boolean} options.closeOnMove 是否在地图移动后关闭信息窗口
* @param {boolean} options.focusAfterOpen 是否尝试聚焦弹出窗口内第一个可聚焦元素
* @param {string} options.className 添加到弹出容器的CSS类名
* @param {string} options.maxWidth 弹出窗口最大宽度,CSS属性字符串,默认'240px'
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer19的点标记数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createMarkerDataLayer(geojson, {
* layerName: 'layer19'
* });
* console.log(layer);
*/
async createMarkerDataLayer(data, options) {
options = options || {};
let type = "marker";
if (options.height) {
options.height = this.map.geoLengthToHeight(options.height);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
color: '#3FB1CE',
closeButton: true,
closeOnClick: true,
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createMarkerDataLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 移除一个功能按钮
* @param {string} name 按钮名
* @example
* // 移除按钮名为"放大一级"的功能按钮
* this.removeButtonItem("放大一级")
*/
removeButtonItem(name, func) {
this.context.ui.removeButtonItem(name, func);
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.addButtonItem(name, func);
});
}
/**
* 采集一个矩形坐标
* @param {boolean} isSnap 是否启用捕捉功能, 默认false
* @returns {Promise<number[] | undefined>} 返回包含四个数字的数组[x1,y1,x2,y2],表示矩形的左下角和右上角坐标,如果取消操作则返回undefined
* @example
* // 采集一个矩形坐标
* const rect = await this.actionDrawRectangle();
* if (!rect) return;
* // 矩形左下角坐标
* const leftBottomX = rect[0];
* const leftBottomY = rect[1];
* // 矩形右上角坐标
* const rightTopX = rect[2];
* const rightTopY = rect[3];
* console.log(leftBottomX, leftBottomY, rightTopX, rightTopY);
*/
async actionDrawRectangle(isSnap) {
this.context.tools.logInfo("请在地图上绘制一个矩形")
let snapObj = {};
if (isSnap) {
// 捕捉dwg图上的点
vjcommon.getMapSnapPoints(this.map, snapObj);
}
let drawRect = await vjmap.Draw.actionDrawRectangle(this.map, isSnap ? {
api: {
getSnapFeatures: snapObj
}
} : {});
if (drawRect.cancel) {
return; // 取消操作
}
let co = drawRect.features[0].geometry.coordinates[0];
co = this.map.fromLngLat(co);
return [co[0].x, co[0].y, co[2].x, co[1].y];
}
/**
* 获取地图的当前缩放级别
* @returns {number} 当前地图的缩放级别
*/
getZoom() { this.map.getZoom() }
/**
* 创建旋转的光环数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {string} options.text 要显示的文字,参数可选,默认为"index ${props.index}"
* @param {number} options.minZoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxZoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isDrawLinePoint 绘制点时包括线上所有点, 参数可选,默认为false
* @param {number} options.width 像素宽度,参数可选,默认为24
* @param {number} options.markerHeight 像素高度,参数可选
* @param {string} options.color1 颜色值一,参数可选
* @param {string} options.color2 颜色值二,参数可选
* @param {number} options.textFontSize 字体大小,参数可选
* @param {string} options.textColor 字体颜色,参数可选
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {string} options.animationType 动画类型,参数可选,无动画 MAP_ANIMATION_NONE,弹跳 MAP_ANIMATION_BOUNCE, 坠落 MAP_ANIMATION_DROP
* @param {number} options.rotation 旋转角度,参数可选
* @param {string} options.rotationAlignment 旋转对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {string} options.pitchAlignment 倾斜对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {number} options.height 坐标标高值,参数可选
* @param {number} options.scaleMaxZoom 能缩放的最大级别,参数可选
* @param {boolean} options.removeWhenNoInMapView 视图范围内自动显隐,参数可选
* @param {number} options.removeWhenNoInMapViewPadding 视图范围内自动显隐Padding像素范围,参数可选,默认500
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer23的旋转的光环数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createRotatingMarkerLayer(geojson, {
* layerName: 'layer23'
* });
* console.log(layer);
*/
async createRotatingMarkerLayer(data, options) {
options = options || {};
let type = "rotatingMarker";
if (options.height) {
options.height = this.map.geoLengthToHeight(options.height);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
text: "ID:${props.index}",
width: 80,
markerHeight: 80,
color1: "#ff0000",
color2: "#ffff00",
textFontSize: 15,
textColor: "#00ffff",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createRotatingMarkerLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 获取地图元数据
* @param {object | object[]} options 参数 可选。
* @param {string} [options.mapid] 地图ID. 可选,为空时则获取当前打开的mapid
* @param {string} [options.version] 地图版本(为空时采用当前打开的地图版本).
* @returns {Promise<Object>} 返回地图元数据, 如果失败则返回undefined, 返回字段如下:
* @example
* // 获取地图sys_hello的元数据
* let res = await this.metadata({
* mapid: "sys_hello"
* });
*/
async metadata(options) {
let svc = this.map.getService();
options = options || {};
let res = await svc.metadata(options.mapid, options.version);
if (res) {
res.bounds = vjmap.GeoBounds.fromString(res.bounds).toArray();
res.dbBounds = vjmap.GeoBounds.fromString(res.dbBounds).toArray();
res.drawBounds = vjmap.GeoBounds.fromString(res.drawBounds).toArray();
res.initBounds = vjmap.GeoBounds.fromString(res.initBounds).toArray();
if (res.clipbounds) {
res.clipbounds = vjmap.GeoBounds.fromString(res.clipbounds).toArray();
} else {
res.clipbounds = null;
}
res.layouts = res.layouts.split(", ");
}
return res;
}
/**
* 创建临时地图, 根据fileid创建一个临时的地图
* @param {string} fileid 文件id
* @param {object} options 其他参数(可选), 支持以下参数:
* @param {boolean} [options.GeomRender] 是否以矢几何渲染打开,, false 内存直接打开,true 几何渲染打开, 默认false
* @param {number} [options.expireTime] 临时图形不浏览情况下过期自动删除时间,单位分钟。默认5
* @param {boolean} [options.isVisible] 是否可见,(可见的话,将可通过ListMaps获取,否则为隐藏图)。默认不可见
* @param {boolean} options.lightTheme 是否使用浅色主题 可选,为空时则使用深色主题
* @returns {string} 返回创建的图的图id
* @example
* // 根据文件id值"c0febd8e49c7"创建一个临时地图
* let mapid = await this.createTempMap("c0febd8e49c7");
* console.log(mapid);
*/
async createTempMap(fileid, options) {
options = options || {};
let svc = this.map.getService().clone();
let mapid = vjmap.getTempMapId(options.expireTime || 5, options.isVisible);
// js代码
let res = await svc.updateMap({
mapid: mapid,
fileid: fileid,
mapopenway: options.GeomRender === true ? vjmap.MapOpenWay.GeomRender : vjmap.MapOpenWay.Memory,
style: options.lightTheme ? vjmap.openMapLightStyle() : vjmap.openMapDarkStyle(),
})
return res.mapid;
}
/**
* 创建线数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {string} options.lineColor 线条颜色,参数可选,默认为'#ff0000'
* @param {number} options.lineWidth 线宽,参数可选,默认为1
* @param {number} options.lineOpacity 线透明度,参数可选,取值范围0-1
* @param {number} options.lineGapWidth 线间隙宽度,参数可选,默认为1,单位像素
* @param {number} options.lineOffset 线的偏移量,参数可选。正值向右/内偏移,负值向左/外偏移
* @param {number} options.lineBlur 线模糊度,参数可选,需小于线宽度
* @param {[number,number]} options.lineDasharray 虚线样式,参数可选,格式如[1,1]表示实线虚线的组合
* @param {[number,number]} options.lineTranslate 平移量,参数可选,格式如[x,y],负数表示左和上,单位像素
* @param {string} options.lineCap 线帽形状,参数可选,可选值:'butt'(尖头)、'round'(圆头)、'square'(平头)
* @param {string} options.lineJoin 拐角形状,参数可选,可选值:'bevel'(平拐)、'round'(圆拐)、'miter'(棱拐)
* @param {string} options.linePattern 线图案,参数可选,需与预加载的图片名称对应,图像宽度必须是2的幂次
* @param {string} options.lineGradient 线渐变,参数可选
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer17的线数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createLineLayer(geojson, {
* layerName: 'layer17'
* });
* console.log(layer);
*/
async createLineLayer(data, options) {
options = options || {};
let type = "line";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
lineColor: '#ff0000',
lineWidth: 1,
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createLineLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 测量面积
* @example
* // 测量面积
* await this.measureArea();
*/
measureArea() {
vjcommon.runMeasureCmd(this.map, "measureArea");
}
/**
* 设置地图深色、浅色或高科技主题
* @param {string} theme 主题,可选值:"dark", "light", "high-tech"
* @example
* // 示例一
* // 设置地图深色主题
* await this.setMapTheme("dark");
* // 示例二
* // 设置地图浅色主题
* await this.setMapTheme("light");
* // 示例三
* // 设置地图高科技主题
* await this.setMapTheme("high-tech");
*/
async setMapTheme(theme) {
if (!this.context.global.updateMapStyleObj) {
this.context.global.updateMapStyleObj = vjcommon.createUpdateMapStyleObj(this.map);
}
const updateMapStyleObj = this.context.global.updateMapStyleObj;
let style = {
backcolor: theme === "light" ? 0xffffff : 0,
};
if (theme === "high-tech") {
style.expression = "var color := gFilterCustomTheme(gInColorRed, gInColorGreen, gInColorBlue, 200, 200, 0.1);gOutColorRed[0] := gRed(color);gOutColorGreen[0] := gGreen(color);gOutColorBlue[0] := gBlue(color);";
}
const curStyle = {...updateMapStyleObj.getCurStyle()};
await updateMapStyleObj.updateStyle(style)
// 添加撤销记录
this.context.ui.addUndoRecord(async () => {
await updateMapStyleObj.updateStyle(curStyle)
});
}
/**
* 设置地图的最大、最小缩放级别
* @param {object} options 包含maxZoom和minZoom的对象
* @param {number} options.maxZoom 要设置的最大缩放级别(可为空)
* @param {number} options.minZoom 要设置的最小缩放级别(可为空)
* @example
* this.setMaxMinZoom({maxZoom: 10}); // 设置最大缩放级别为10
*/
setMaxMinZoom(options) {
options = options || {};
if (options.maxZoom !== undefined) {
let oldMaxZoom = this.map.getMaxZoom(); // 获取当前最大缩放级别
this.map.setMaxZoom(options.maxZoom); // 设置新的最大缩放级别
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.setMaxZoom(oldMaxZoom);
});
}
if (options.minZoom !== undefined) {
let oldMinZoom = this.map.getMinZoom(); // 获取当前最小缩放级别
this.map.setMinZoom(options.minZoom); // 设置新的最小缩放级别
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.setMinZoom(oldMinZoom);
});
}
}
/**
* 从服务端获取用户自定义数据
* @param {string | string[]} key 键名,如果是数组的话,可以查询.
* @param {object} options 选项 retDataType为空时查询数据及属性,"value"只查询数据,"prop"只查询属性。contentType返回类型,为空时为json,为"image"时为图片.
* @returns {Promise<object>} 返回获取到的用户自定义数据,格式如下 {data: any, prop: any, status: string}
* @example
* // 从服务端获取键名为testKey的用户自定义数据
* let data = await this.getCustomDataServer("testKey");
* console.log(data);
* // 从服务端批量获取用户自定义数据,键名为testKey1,testKey2
* let dataArray = await this.getCustomDataServer(["testKey1", "testKey2"]);
* console.log(dataArray);
*/
async getCustomDataServer(key, options) {
let svc = this.map.getService();
return await svc.getCustomData(key, options);
}
/**
* 设置地图是否显示线宽
* @param {boolean} isShowLineWidth 是否显示线宽
* @example
* // 设置地图显示线宽
* await this.setMapShowLineWidth(true);
* // 设置地图不显示线宽
* await this.setMapShowLineWidth(false);
*/
async setMapShowLineWidth(isShowLineWidth) {
if (!this.context.global.updateMapStyleObj) {
this.context.global.updateMapStyleObj = vjcommon.createUpdateMapStyleObj(this.map);
}
const updateMapStyleObj = this.context.global.updateMapStyleObj;
const oldLineweight = updateMapStyleObj.getCurStyle()?.lineweight;
await updateMapStyleObj.updateStyle({
lineweight: isShowLineWidth ? 1 : 0
})
// 添加撤销记录
this.context.ui.addUndoRecord(async () => {
await updateMapStyleObj.updateStyle({
lineweight: oldLineweight
})
});
}
/**
* 删除地图
* @param {object} options 参数
* @param {string} options.mapid 地图ID
* @param {string} options.version 版本号,如删除所有版本,输入"*"号
* @param {number} [options.retainVersionMaxCount] 删除所有版本时,保留的最新的版本总数。可选
* @returns {Promise<boolean>} 返回删除结果true表示成功,false表示失败
* @example
* // 示例一
* // 删除地图hello的所有版本
* let res1 = await this.deleteMap({
* mapid: "hello",
* version: "*"
* });
* console.log(res1);
* // 示例二
* // 删除地图hello的v1版本
* let res2 = await this.deleteMap({
* mapid: "hello",
* version: "v1"
* });
* console.log(res2);
*/
async deleteMap(options) {
let svc = this.map.getService();
let res = await svc.cmdDeleteMap(options.mapid, options.version, options.retainVersionMaxCount);
return res && res.status;
}
/**
* 创建呼吸的光圈点数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {string} options.text 要显示的文字,参数可选,默认为"index ${props.index}"
* @param {number} options.minZoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxZoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isDrawLinePoint 绘制点时包括线上所有点, 参数可选,默认为false
* @param {number} options.width 像素宽度,参数可选,默认为24
* @param {number} options.markerHeight 像素高度,参数可选
* @param {string} options.color1 颜色值一,参数可选
* @param {string} options.color2 颜色值二,参数可选
* @param {number} options.textFontSize 字体大小,参数可选
* @param {string} options.textColor 字体颜色,参数可选
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {string} options.animationType 动画类型,参数可选,无动画 MAP_ANIMATION_NONE,弹跳 MAP_ANIMATION_BOUNCE, 坠落 MAP_ANIMATION_DROP
* @param {number} options.rotation 旋转角度,参数可选
* @param {string} options.rotationAlignment 旋转对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {string} options.pitchAlignment 倾斜对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {number} options.height 坐标标高值,参数可选
* @param {number} options.scaleMaxZoom 能缩放的最大级别,参数可选
* @param {boolean} options.removeWhenNoInMapView 视图范围内自动显隐,参数可选
* @param {number} options.removeWhenNoInMapViewPadding 视图范围内自动显隐Padding像素范围,参数可选,默认500
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer6的呼吸的光圈点数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createBreathingMarkerLayer(geojson, {
* layerName: 'layer6',
* text: '索引:${props.index}'
* });
* console.log(layer);
*/
async createBreathingMarkerLayer(data, options) {
options = options || {};
let type = "breathingMarker";
if (options.height) {
options.height = this.map.geoLengthToHeight(options.height);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
"width": 80,
"markerHeight": 80,
"color1": "#ff0000",
"color2": "#ffff00",
"textFontSize": 15,
"textColor": "#00ffff",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createBreathingMarkerLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 采集一个点坐标
* @param {boolean} isSnap 是否启用捕捉功能, 默认false
* @returns {Promise<[number,number] | undefined>} 返回采集的坐标[x,y],如果取消操作则返回undefined
* @example
* const pt = await this.actionDrawPoint(true); // 启用捕捉功能采集一个点坐标
* if (!pt) return;
* // 点的坐标
* const x = pt[0];
* const y = pt[1];
* console.log(x, y);
*/
async actionDrawPoint(isSnap) {
this.context.tools.logInfo("请在地图上绘制一个点")
let snapObj = {};
if (isSnap) {
// 捕捉dwg图上的点
vjcommon.getMapSnapPoints(this.map, snapObj);
}
let drawPoint = await vjmap.Draw.actionDrawPoint(this.map, isSnap ? {
api: {
getSnapFeatures: snapObj
}
} : {});
if (drawPoint.cancel) {
return; // 取消操作
}
let co = drawPoint.features[0].geometry.coordinates;
co = this.map.fromLngLat(co);
return [co.x, co.y];
}
/**
* 根据图层名称和数据内容更新数据图层数据值
* @param {string} layerName 图层名称,参数必填
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @example
* // 更新图层名称为layer1的数据图层数据为随机生成的GeoJson数据
* let layerName = 'layer1';
* let geojson = this.randomGeojsonInMapBounds();
* this.updateDataLayerSource(layerName, geojson);
*/
updateDataLayerSource(layerName, data) {
let item = this.context.global.layers.find(layer => layer.layerName == layerName);
if(item) {
item.data = data;
let oldData = {...item.data};
let layerId = item.layerId;
let sourceId = item.sourceId;
if (item.layer && typeof(item.layer.getLayerId) == "function") {
layerId = item.layer.getLayerId();
let style = this.map.getStyle();
let layer = style.layers.find(layer => layer.id == layerId);
if (layer && layer.source) {
sourceId = layer.source;
}
}
this.map.setData(sourceId, this.map.toLngLat(data));
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.updateDataLayerSource(layerName, oldData);
});
}
}
/**
* 把geojson数据按给定的四参数进行转换
* @param {Object} data 要转换的geojson数据,参数必填
* @param {Object} transform 要转换的四参数,平移{dx: number, dy: number},缩放{scale: number},旋转弧度{rotate: number},参数必填
* @param {boolean} isLngLatCoord 传入的是否为经纬度坐标,默认false为CAD图几何坐标,true为地图经纬度坐标,参数可选
* @returns {Object} 返回转换后的geojson数据
* @example
* // 把geojson数据按给定的四参数进行转换,平移[100, 200],缩放 0.5,旋转0.5弧度
* let newGeojson = this.geojsonTransform(geojson, {dx: 100, dy: 200, scale: 0.5, rotate: 0.5});
* console.log(newGeojson);
*/
geojsonTransform(data, transform, isLngLatCoord) {
return vjmap.transform.convert(data, (pt) => {
let point = isLngLatCoord === true ? this.map.fromLngLat(vjmap.geoPoint(pt)) : vjmap.geoPoint(pt);
let newPoint = vjmap.coordTransfromByFourParamter(point, {
dx: transform.dx || 0,
dy: transform.dy || 0,
scale: transform.scale || 1,
rotate: transform.rotate || 0
});
return isLngLatCoord === true ? this.map.toLngLat(newPoint) : [newPoint.x, newPoint.y];
});
}
/**
* 采集三维多段线
* @param {object} options 参数
* @param {number} options.searchRadiusRatio 搜索文字范围半径倍数,默认2
* @param {boolean} isSnap 是否启用捕捉绘制的点, 默认true
* @param {boolean} isSnapMap 是否启用捕捉图上的点 默认false
* @example
* // 采集三维多段线
* await this.pick3dPolylines();
*/
async pick3dPolylines(options) {
// --采集三维线--采集时可自动搜索文字获取当前点的高程值
// 采集的坐标转成 [x,z,y]格式,可参考此示例自动生成三维管道
// https://vjmap.com/map3d/demo/#/demo/this.map/builtin/objects/03builtinobjtubepath
options = options || {};
let svc = this.map.getService();
let mapAllTexts;
let textMeanHeight = 0; // 图中文字平均高
const getAllTexts = async ()=> {
if (mapAllTexts) return mapAllTexts; // 如果获取过了
// 实体类型ID和名称映射
const { entTypeIdMap } = await svc.getConstData();
const getTypeNameById = name => Object.keys(entTypeIdMap).find(e => entTypeIdMap[e] === name);
let queryTextEntTypeId = getTypeNameById("AcDbText"); // 单行文字
let queryMTextEntTypeId = getTypeNameById("AcDbMText"); // 多行文字
let queryAttDefEntTypeId = getTypeNameById("AcDbAttributeDefinition"); // 属性定义文字
let queryAttEntTypeId = getTypeNameById("AcDbAttribute"); // 属性文字
let query = await svc.conditionQueryFeature({
condition: `name='${queryTextEntTypeId}' or name='${queryMTextEntTypeId}' or name='${queryAttDefEntTypeId}' or name='${queryAttEntTypeId}'`, // 只需要写sql语句where后面的条件内容,字段内容请参考文档"服务端条件查询和表达式查询"
fields: "objectid,envelop,text",
geom: true,
limit: 100000 //设置很大,相当于把所有的圆都查出来。不传的话,默认只能取100条
})
if (query.error) {
this.map.logInfo(query.error, "error");
return;
} else {
mapAllTexts = []
textMeanHeight = 0
if (query.recordCount > 0) {
for(let i = 0; i < query.recordCount; i++) {
let bounds = this.map.getEnvelopBounds(query.result[i].envelop);
mapAllTexts.push({
position: bounds.center(),
text: query.result[i].text
});
textMeanHeight += bounds.height()
}
textMeanHeight = textMeanHeight / query.recordCount
}
}
return mapAllTexts
}
function isNumeric(value) {
const regex = /^[0-9]*\.?[0-9]+$/; // 匹配数字和一个小数点的模式
return regex.test(value);
}
let snapObj = {};
const collect3Dlines = async () => {
// 给地图发送ESC键消息即可取消,模拟按ESC键,如果之前在采集,先取消之前的
this.map.fire("keyup",{keyCode:27})
this.map.logInfo("请在图上采集线,按回车键或右键菜单里确认结束")
let elevations = []
if (options.isSnapMap && !snapObj.features) {
vjcommon.getMapSnapPoints(this.map, snapObj);
}
let line = await vjmap.Draw.actionDrawLineSting(this.map, {
snap: options.isSnap === false ? false : true,
api: {
getSnapFeatures: snapObj
},
updatecoordinate: async (e) => {
if (!e.lnglat) return;
if (e.isClick) {
let curIdx = e.feature.coordinates.length - 1
const co = this.map.fromLngLat(e.feature.coordinates[curIdx]);
// 获取当前点旁边所有的文字
let allTexts = await getAllTexts()
let radius = textMeanHeight * (options.searchRadiusRatio || 2); // 以文字平均高度的2倍做为搜索距离 这个数值根据实际情况调节
// 先根据是否是数字,再根据距离过滤
let texts = allTexts.filter(item => isNumeric(item.text)).filter(item => vjmap.geoPoint(co).distanceTo(item.position) <= radius)
texts = texts.map(t => t.text); // 只获取内容
// 弹出输入框,并获取用户输入
let elevation = await this.context.ui.selectSuggestionValue(texts, {mustNumber: true, title: "请选择或输入高程值"})
if (elevation === undefined) {
// 取消采集
this.map.fire("keyup",{keyCode:27})
return;
}
elevations.push(elevation)
}
}, contextMenu: (e) => {
new vjmap.ContextMenu({
event: e.event.originalEvent,
theme: "dark", //light
width: "250px",
items: [
{
label: '确认',
onClick: () => {
// 给地图发送Enter键消息即可取消,模拟按Enter键
this.map.fire("keyup", { keyCode: 13 })
}
},
{
label: '取消',
onClick: () => {
// 给地图发送ESC键消息即可取消,模拟按ESC键
this.map.fire("keyup", { keyCode: 27 })
}
}, {
label: '删除上一个点',
onClick: () => {
// 给地图发送退格键Backspace消息即可删除上一个点,模拟按Backspace键
this.map.fire("keyup", { keyCode: 8 })
elevations.pop(); // 删除最后一个元素
}
}, {
label: '结束',
onClick: () => {
// 给地图发送ESC键消息即可取消,模拟按ESC键
this.map.fire("keyup", { keyCode: 27 })
}
}
]
});
}
})
if (line.cancel) {
return // 取消操作
}
let feature = line.features[0]
feature.properties.elevations = elevations;
feature.properties.typename = "3dpolyline"
feature.properties.line_width = 12;
feature.properties.color = vjmap.randomColor()
this.map.getDrawLayer().add(feature)
if (options.isSnap !== false) {
// 把绘制的线加进捕捉
snapObj.features = snapObj.features || [];
snapObj.features.push({
type: "Feature",
geometry: {
type: "LineString",
coordinates: feature.geometry.coordinates
}
});
}
}
this.addButtonItem("开始采集", async () => {
await collect3Dlines()
})
this.addButtonItem("获取三维线数据", async () => {
let data = this.map.getDrawLayer().getAll();
data = this.map.fromLngLat(data);
let features = data.features.filter(item => item.properties.typename === "3dpolyline") || [];
let result = [];
for(let i = 0; i < features.length; i++) {
let coords = features[i].geometry.coordinates;
let elevations = features[i].properties.elevations;
let data = coords.map((co, idx) => [co[0], co[1], elevations[idx]]);
result.push({
paths: data,
name: i
})
}
await this.showDataInfoDialog(result, false, "三维线数据")
})
this.addButtonItem("退出采集", async () => {
// 取消采集
this.map.fire("keyup",{keyCode:27})
let data = this.map.getDrawLayer().getAll();
data.features = data.features.filter(item => item.properties.typename !== "3dpolyline")
this.map.getDrawLayer().set(data);
this.removeButtonItem("开始采集")
this.removeButtonItem("获取三维线数据")
this.removeButtonItem("退出采集")
})
}
/**
* 根据内容生成二维码图片
* @param {string} content 二维码内容
* @param {object} options 选项
* @param {number} options.size 二维码大小,默认256
* @param {boolean} options.disableOpen 只返回地址,不打开图片,默认false
* @returns {Promise<string>} 返回二维码图片的url
* @example
* // 根据"https://www.vjmap.com"生成二维码图片
* let url = await this.qrcodeUrl("https://www.vjmap.com");
* console.log(url);
*/
async qrcodeUrl(content, options) {
options = options || {};
let svc = this.map.getService();
let res = await svc.qrcodeUrl(content, options.size);
if (options.disableOpen) return res;
window.open(res);
return res;
}
/**
* 删除一个点标记Marker
* @param {string} id 点标记Marker的id
* @example
* this.deleteMarker("marker_1"); // 删除id为marker_1的点标记
*/
deleteMarker(id) {
let markers = this.map.getMarkers();
let marker = markers.find(m => m.id == id);
if (marker) {
let point = this.map.fromLngLat(marker.getLngLat());
let oldOptions = {};
oldOptions.color = marker._color;
oldOptions.data = marker.data;
oldOptions.draggable = marker.isDraggable();
oldOptions.rotation = marker.getRotation();
if (marker.getPopup()) {
marker.getPopup().remove();
}
marker.remove()
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.addMarker(point, oldOptions);
});
}
}
/**
* 将CAD图几何坐标转换为地图经纬度坐标
* @returns {[number, number]} 返回包含两个数字的数组[lng,lat],表示地图经纬度坐标
* @example
* // 将CAD图几何坐标[1000, 2000]转换为地图经纬度坐标
* let point = this.geoPointToLngLat([1000, 2000]);
* let lng = point[0];
* let lat = point[1];
* console.log(lng, lat);
*/
geoPointToLngLat(pt) {
let lngLat = this.map.toLngLat(pt);
let lng = lngLat[0];
let lat = lngLat[1];
return [lng, lat];
}
/**
* 增加地图全屏控件
* @param {string} options.container 全屏的容器,参数可选
* @param {'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'} options.position 控件位置,参数可选, 默认'top-right'
* @example
* // 增加地图全屏控件,位置左上角
* this.addMapFullscreenControl({ position: 'top-left' });
*/
addMapFullscreenControl(options) {
options = options || {};
this.context.global.controls = this.context.global.controls || {};
if (this.context.global.controls.fullscreenControl) {
this.map.removeControl(this.context.global.controls.fullscreenControl.control);
}
if (options.container) {
options.container = document.querySelector(options.container);
}
let fullscreenControl = new vjmap.FullscreenControl(options);
this.map.addControl(fullscreenControl, options.position);
this.context.global.controls.fullscreenControl = {
control: fullscreenControl,
position: options.position,
options: options
};
// 增加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.removeControl(fullscreenControl);
delete this.context.global.controls.fullscreenControl;
});
}
/**
* 手动交互式绘制一个多边形
* @param {object} options 配置参数, 参数可选
* @param {boolean} options.isSnap 是否启用捕捉功能, 参数可选,默认false
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {boolean} options.isFill 是否填充,默认true,参数可选
* @param {number} options.opacity 透明度,默认1,参数可选
* @param {number} options.extrusionHeight 拉伸高度,有拉伸高度为拉伸对象,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 手动绘制一个多边形
* const polygon = await this.drawPolygon({
* isSnap: true,
* color: "#FF0000",
* isFill: true,
* opacity: 0.8
* });
* if (!polygon) return;
* console.log("多边形ID:", polygon.id);
* console.log("多边形的点坐标:", polygon.points);
*/
async drawPolygon(options) {
let points = await this.actionDrawPolygon(options.isSnap);
if (!points) return;
return this.createPolygon(points, options);
}
/**
* 创建点符号聚合数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {string} options.iconImage 图标名称,用于绘制图像背景的图像名称
* @param {number} options.iconRotate 图标旋转角度,顺时针旋转,单位为角度,默认0
* @param {[number,number]} options.iconOffset 图标与锚点偏移距离,[x,y]格式,正值向右和向下
* @param {string} options.iconAnchor 图标锚定位置,可选值: "center", "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right"
* @param {string} options.iconRotationAlignment 图标旋转对齐方式,可选值: "map", "viewport", "auto"
* @param {string} options.iconPitchAlignment 地图倾斜时图标方向,可选值: "map", "viewport", "auto"
* @param {boolean} options.iconAllowOverlap 是否允许图标重叠,默认false
* @param {number} options.iconOpacity 图标透明度,0-1之间,默认1
* @param {boolean} options.iconIgnorePlacement 是否忽略碰撞检测,默认false
* @param {boolean} options.iconOptional 图标重叠选项,默认false
* @param {number} options.iconSize 图标缩放比例,默认1
* @param {string} options.iconTextFit 图标适应文本方式,可选值: "none", "width", "height", "both"
* @param {[number,number,number,number]} options.iconTextFitPadding 图标文本匹配padding,[top, right, bottom, left]格式
* @param {number} options.iconPadding 图标边界填充大小,默认1像素
* @param {boolean} options.iconKeepUpright 是否防止图标倒置,默认false
* @param {string} options.iconColor SDF图标颜色,默认"#000000"
* @param {string} options.iconHaloColor SDF图标光晕颜色,默认"rgba(0, 0, 0, 0)"
* @param {number} options.iconHaloWidth 图标光晕宽度,默认0
* @param {number} options.iconHaloBlur 图标光晕模糊度,默认0
* @param {[number,number]} options.iconTranslate 图标平移距离,[x,y]格式
* @param {string} options.iconTranslateAnchor 图标平移参考系,可选值: "map", "viewport"
* @param {string|Expression} options.textField 文本标签内容
* @param {string[]} options.textFont 文本字体,默认["Open Sans Regular","Arial Unicode MS Regular"]
* @param {number} options.textSize 文本大小,默认16
* @param {boolean} options.textAllowOverlap 是否允许文本重叠,默认false
* @param {string} options.textColor 文本颜色,默认"#000000"
* @param {number} options.textOpacity 文本透明度,0-1之间,默认1
* @param {[number,number]} options.textOffset 文本偏移,[x,y]格式
* @param {number} options.textRotate 文本旋转角度
* @param {number} options.textPadding 文本内边距,默认2
* @param {boolean} options.textKeepUpright 是否防止文本倒置,默认true
* @param {number} options.textMaxWidth 文本最大宽度,默认10em
* @param {number} options.textLineHeight 文本行高,默认1.2
* @param {number} options.textLetterSpacing 文字间距,默认0
* @param {string} options.textJustify 文本对齐方式,可选值: "auto", "left", "center", "right"
* @param {string} options.textAnchor 文本锚点位置
* @param {string} options.textRotationAlignment 文本旋转对齐方式
* @param {string} options.textPitchAlignment 文本倾斜对齐方式
* @param {number} options.textMaxAngle 相邻字符最大角度,默认45°
* @param {string[]} options.textWritingMode 文本书写模式
* @param {string} options.textTransform 文本转换方式,可选值: "none", "uppercase", "lowercase"
* @param {number} options.textRadialOffset 文本径向偏移
* @param {string[]} options.textVariableAnchor 文本可变锚点位置数组
* @param {boolean} options.textIgnorePlacement 是否忽略文本位置
* @param {boolean} options.textOptional 文本显示选项
* @param {string} options.textHaloColor 文本光晕颜色
* @param {number} options.textHaloWidth 文本光晕宽度
* @param {number} options.textHaloBlur 文本光晕模糊度
* @param {[number,number]} options.textTranslate 文本平移距离
* @param {string} options.textTranslateAnchor 文本平移参考系
* @param {string} options.symbolPlacement 符号放置位置,可选值: "point", "line", "line-center"
* @param {number} options.symbolSpacing 符号间距,默认250
* @param {boolean} options.symbolAvoidEdges 是否避免边缘碰撞
* @param {string} options.symbolZOrder 符号绘制顺序,可选值: "auto", "viewport-y", "source"
* @param {number} options.clusterMaxZoom 聚合最大缩放级别,超过此级别不再聚合,默认10
* @param {number} options.clusterRadius 聚合的半径范围,单位像素,默认60
* @param {Array<[number, string]>} options.outerColors 聚合外部圆的值和颜色分级设置,格式为[[值, 颜色], ...],默认[[1000, 'rgba(253, 156, 115, 0.6)'], [100, 'rgba(241, 211, 87, 0.6)'], [0, 'rgba(181, 226, 140, 0.6)']]
* @param {number} options.outerCircleRadius 聚合外部圆的半径,单位像素,默认20
* @param {Array<[number, string]>} options.innerColors 聚合内部圆的值和颜色分级设置,格式为[[值, 颜色], ...],默认[[1000, 'rgba(241, 128, 23, 0.6)'], [100, 'rgba(240, 194, 12, 0.6)'], [0, 'rgba(110, 204, 57, 0.6)']]
* @param {number} options.innerCircleRadius 聚合内部圆的半径,单位像素,默认15
* @param {number} options.clusterTextSize 聚合文本的字体大小,单位像素,默认12
* @param {string} options.color 标记颜色,参数可选,默认为'#3FB1CE'
* @param {number[]} options.offset 偏离XY像素值,如[0,0],负数表示向左和向上
* @param {number} options.rotation 标记旋转角度,顺时针旋转,默认0
* @param {string} options.anchor 标记锚点位置,可选值:center、top、bottom、left、right、top-left、top-right、bottom-left、bottom-right,默认center
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {string} options.animationType 动画类型:MAP_ANIMATION_NONE(无动画)、MAP_ANIMATION_BOUNCE(弹跳)、MAP_ANIMATION_DROP(坠落)
* @param {string} options.rotationAlignment 旋转对齐:auto(自动)、map(相对地图)、viewport(相对视口),默认auto
* @param {string} options.pitchAlignment 倾斜对齐:auto(自动)、map(相对地图)、viewport(相对视口),默认auto
* @param {number} options.scale 缩放比例,默认1,对应高度41px宽度27px
* @param {number} options.scaleMaxZoom 能缩放的最大级别(0-24),小于此级别时自动缩放
* @param {number} options.height 标高值,单位CAD图制图单位
* @param {boolean} options.removeWhenNoInMapView 是否在视图范围内自动显隐
* @param {number} options.removeWhenNoInMapViewPadding 视图范围内自动显隐的padding像素值,默认500
* @param {number} options.clickTolerance 点击像素容差,默认继承地图的clickTolerance
* @param {string} options.customImage 自定义图片地址/base64/svg内容
* @param {number} options.customImageWidth 自定义图片宽度
* @param {number} options.customImageHeight 自定义图片高度
* @param {string} options.customHtml 自定义Html元素的JS代码
* @param {string} options.popupHtml 信息窗口HTML内容
* @param {[number,number]} options.popupOffset 信息窗口偏离XY像素值
* @param {string} options.popupAnchor 信息窗口锚点位置,同anchor参数
* @param {boolean} options.closeButton 是否显示关闭按钮
* @param {boolean} options.closeOnClick 是否在点击地图时关闭弹出窗口
* @param {boolean} options.closeOnMove 是否在地图移动后关闭信息窗口
* @param {boolean} options.focusAfterOpen 是否尝试聚焦弹出窗口内第一个可聚焦元素
* @param {string} options.className 添加到弹出容器的CSS类名
* @param {string} options.maxWidth 弹出窗口最大宽度,CSS属性字符串,默认'240px'
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer25的点符号聚合数据图层, 图标名称为_markerIcon_
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createSymbolClusterDataLayer(geojson, {
* layerName: 'layer25',
* iconImage: '_markerIcon_'
* });
* console.log(layer);
*/
async createSymbolClusterDataLayer(data, options) {
options = options || {};
let type = "symbolCluster";
if (options.height) {
options.height = this.map.geoLengthToHeight(options.height);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
clusterMaxZoom: 10,
clusterRadius: 60,
outerColors: [[1000, 'rgba(253, 156, 115, 0.6)'], [100, 'rgba(241, 211, 87, 0.6)'], [0, 'rgba(181, 226, 140, 0.6)']],
outerCircleRadius: 20,
innerColors: [[1000, 'rgba(241, 128, 23, 0.6)'], [100, 'rgba(240, 194, 12, 0.6)'], [0, 'rgba(110, 204, 57, 0.6)']],
innerCircleRadius: 15,
clusterTextSize: 12,
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createSymbolClusterDataLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 获取地图的中心坐标
* @returns {[number, number]} 当前地图中心的坐标,格式为[x,y]
*/
getCenter() {
let center = this.map.fromLngLat(this.map.getCenter());
return [center.x, center.y];
}
/**
* 自动提取图中的表格
* @returns {Promise<GeoJSON>} 返回提取出的表格的GeoJSON数据, 如果失败则返回undefined
* @example
* //示例一
* // 自动提取图中的表格
* let geojson = await this.extractTale();
* if (!geojson) {
* console.log("提取表格失败");
* } else {
* console.log("提取表格成功, geojson数据为:", geojson);
* }
* //示例二
* // 手动选择范围自动提取图中的表格
* let res2 = await this.extractTale({
* isSelectBounds: true
* });
* console.log(res2);
*/
async extractTale(options) {
options = options || {};
let svc = this.map.getService();
options = {
mapid: options.mapid || svc.currentMapParam().mapid,
bounds: "",
condition: "",
debugInfo: [],
digit: 4,
tol: 0,
tableEdgeMinPoint: 12,
tableTextMinCount: 4,
cellMaxArea: 90,
cellEmptyRatio: 90,
unLinkLineRatio: 20,
tableMaxCellCount: 100000,
noHvLineSegCount: 0,
seachTableMost: false,
version: svc.currentMapParam()?.version,
layer: svc.currentMapParam()?.layer,
// 如果直接内存打开的话,用表达式查询,如果几何渲染打开,用空间查询
geom: svc.currentMapParam()?.mapopenway != vjmap.MapOpenWay.Memory,
...options,
};
if (options.isSelectBounds) {
// 如果是手动,先在图中选择一个区域
if (this.map.logInfo) this.map.logInfo("请先选择表格所在的矩形区域,选择区域的范围需要覆盖表格内容", "success", 10000);
// 先在地图上绘制一个矩形,做为裁剪区域
let snapObj = {}; // 设置的捕捉的实体
vjcommon.getMapSnapPoints(this.map, snapObj);
let res = await vjmap.Draw.actionDrawRectangle(this.map, {
api: {
getSnapFeatures: snapObj //要捕捉的数据项在后面,通过属性features赋值
},
contextMenu: (e) => {
// 给地图发送Enter键消息即可取消,模拟按Enter键
this.map.fire("keyup", {keyCode:13})
}
});
if (res.cancel) {
return // 取消操作
}
// 获取所绘制的坐标
let rectCoord = this.map.fromLngLat(res.features[0].geometry.coordinates[0]);
// 取左下角和右下角两个点
let bounds = [rectCoord[0].x, rectCoord[0].y, rectCoord[2].x, rectCoord[2].y];
options.bounds = bounds.join(',');
}
if (Array.isArray(options.bounds)) {
options.bounds = JSON.stringify(options.bounds);
}
if (options.showSettingForm !== false) {
options = await this.showDataModifyDlg({
data: options,
title: "自动提取表格设置",
showForm: true
})
if (!options) return;
}
let result = await svc.cmdExtractTale(options);
if (result && result.error) {
if (this.map.logInfo) this.map.logInfo(result.error, "error", 10000);
return;
}
let tables = result.tables || [];
let geoDatas = {
"type": "FeatureCollection",
"features": []
}
for(let t = 0; t < tables.length; t++) {
let rect = vjmap.GeoBounds.fromString(tables[t].rect);
let pts = rect.toPointArray();
pts.push(pts[0]); // 闭合
geoDatas.features.push(
{
"id": t,
"type": "Feature",
"properties": {
color: vjmap.randomColor(),
index: t,
data: tables[t],
tableDatas: tables[t].datas
},
"geometry": {
"coordinates": [pts],
"type": "Polygon"
}
})
}
return geoDatas;
}
/**
* 显示消息内容
* @param {string} message 消息内容,必填
* @param {string} type 消息类型,可选值为警告("warning")、错误("error")、信息("info")、成功("success"),参数可选, 默认错误
* @example
* // 示例一
* // 显示错误消息内容"出错了"
* this.showMessage("出错了", "error");
* // 示例二
* // 显示成功消息内容"已完成"
* this.showMessage("已完成", "success");
*/
async showMessage(message, type) {
return await this.context.ui.showMessage(message, type || "error");
}
/**
* 从服务端删除用户自定义数据
* @param {string | string[]} key 键名,如果是数组的话,可以查询
* @param {boolean} isPrefix 是否删除所有前缀为key的所有键值,默认false
* @returns {Promise<boolean>} 返回删除是否成功, true表示成功,false表示失败
* @example
* // 从服务端删除键名为drawData的用户自定义数据
* await this.deleteCustomDataServer("drawData");
*/
async deleteCustomDataServer(key, isPrefix = false) {
let svc = this.map.getService();
let res = await svc.deleteCustomData(key, isPrefix);
return res?.status === "ok";
}
/**
* 创建栅格瓦片图层
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {number} options.rasterOpacity 图片的不透明度,参数可选,取值范围为 0 ~ 1,默认值为 1
* @param {number} options.rasterHueRotate 在色轮上旋转色相的角度,参数可选,默认值为 0,单位:角度
* @param {number} options.rasterBrightnessMin 图片的最小亮度,参数可选,取值范围为 0 ~ 1,默认值为 0
* @param {number} options.rasterBrightnessMax 图片的最大亮度,参数可选,取值范围为 0 ~ 1,默认值为 1
* @param {number} options.rasterSaturation 图片的饱和度,参数可选,取值范围为 -1 ~ 1,默认值为 0
* @param {number} options.rasterContrast 图片的对比度,参数可选,取值范围为 -1 ~ 1,默认值为 0
* @param {number} options.rasterFadeDuration 切换瓦片时的渐隐时间,参数可选,默认值为 300,单位:毫秒
* @param {string} options.rasterResampling 采样方式,参数可选,可选值为 linear、nearest,默认值为 linear
* @param {string} options.tileMapId 瓦片的mapid,参数可选
* @param {string} options.tileMapVersion 瓦片的版本,参数可选
* @param {string} options.tileMapLayer 瓦片的样式图层名称,参数可选
* @param {string|Array<string>} options.tiles 瓦片地址,支持变量 {x},{y},{z},{serviceUrl},{accessToken}
* @param {number} options.tileSize 瓦片大小,参数可选,默认值为 512
* @param {number[]} options.bounds 瓦片范围,参数可选,格式为[minx, miny, maxx, maxy],默认为全图范围
* @param {string} options.scheme 瓦片y方向,参数可选,可选值为 xyz(Y从顶部开始)、tms(Y从底部开始),默认为 xyz
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 创建栅格瓦片图层, 瓦片的mapid为sys_hello
* let layer = await this.createRasterLayer({
* tileMapId: "sys_hello",
* });
* console.log(layer);
*/
async createRasterLayer(options) {
options = options || {};
let type = "raster";
let randomId = vjmap.RandomID();
let sourceId = `source_${type}_${randomId}`;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
sourceId,
layerId,
...options,
type: type
}
if (options.tileMapId && !options.tiles) {
let layer = options.tileMapLayer;
if (!layer) {
let style = await this.map.getService().createStyle({
backcolor: this.map.getService().currentMapParam().darkMode ? 0x000000 : 0xFFFFFF
}, options.tileMapId, options.tileMapVersion);
layer = style.stylename
}
let tileUrl = this.map.getService().rasterTileUrl({
mapid: options.tileMapId,
version: options.tileMapVersion,
layer: layer,
})
options.tiles = tileUrl;
}
if (options.bounds) {
const p1 = this.map.toLngLat([options.bounds[0], options.bounds[1]]);
const p2 = this.map.toLngLat([options.bounds[2], options.bounds[3]]);
options.bounds = [p1[0], p1[1], p2[0], p2[1]];
}
let sourceData = {
type: "raster",
tiles: Array.isArray(options.tiles) ? options.tiles : [options.tiles],
tileSize: options.tileSize || 256
}
if (options.bounds) {
sourceData.bounds = options.bounds;
}
if (options.scheme) {
sourceData.scheme = options.scheme;
}
this.map.addRasterSource(sourceId, sourceData);
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data: sourceData, options, layer, visible: true, create: this.createRasterLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 通过数据内容显示数据面板
* @param {any} data 数据内容,必填
* @param {object} options 数据面板配置,可选
* @param {string} options.title 数据面板标题,可选
* @param {number} options.width 数据面板宽度,可选
* @param {number} options.height 数据面板高度,可选
* @param {number} options.left 数据面板左边距离,可选
* @param {number} options.top 数据面板上边距离,可选
* @param {string} options.position 数据面板位置, 值有'top-right'、'top-left'、'bottom-right'、'bottom-left',可选,默认"top-left"
* @param {boolean} options.showForm 是否以表单形式显示,可选,默认false
* @example
* // 通过数据内容显示数据面板,数据为{"a":1,"b":2},数据面板标题为"数据查看",数据面板宽度为300,数据面板高度为200,数据面板左边距离为50,数据面板上边距离为100,数据面板位置为左上角,数据面板以表单形式显示
* this.showDataPanel({a:1,b:2}, {title: "数据查看", width: 300, height: 200, left: 50, top: 100, position: "top-left", showForm: true})
*/
showDataPanel(data, options) {
options = options || {};
options.title = options.title || "数据查看";
this.context.ui.setDataPanelContent(data, options);
}
/**
* 设置地图的中心坐标
* @param {[number, number]} point 中心点坐标,格式为[x,y]
* @example
* this.setCenter([100, 20]); // 设置中心坐标为x为100,y为20
*/
setCenter(point) {
let oldCenter = this.map.fromLngLat(this.map.getCenter()) ; // 获取当前中心坐标
this.map.setCenter(this.map.toLngLat(point)); // 设置新的中心坐标
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.setCenter(this.map.toLngLat(oldCenter));
});
}
/**
* 清空地图数据
* @param {object} options 选项
* @param {boolean} [options.clearMarkers] 是否清空所有点标注
* @param {boolean} [options.clearDraw] 是否清空所有绘制图层
* @param {boolean} [options.clearOtherLayerData] 清空地图除底图外所有数据
* @param {boolean} [options.clearAllData] 清空地图所有数据
* @example
* // 示例一
* // 清空地图所有点标注数据
* this.clearMapData({clearMarkers: true});
* // 示例二
* // 清空地图所有绘制图层数据
* this.clearMapData({clearDraw: true});
* // 示例三
* // 清空地图除底图外所有数据
* this.clearMapData({clearOtherLayerData: true});
* // 示例四
* // 清空地图所有数据
* this.clearMapData({clearAllData: true});
*/
clearMapData(options) {
options = options || {};
if (options.clearMarkers === true) {
this.map.removeMarkers();
this.map.removePopups();
}
if (options.clearDraw === true) {
let drawLayer = this.map.getDrawLayer();
drawLayer.deleteAll();
}
if (options.clearOtherLayerData === true) {
this.map.removeMarkers();
this.map.removePopups();
// 把所有绘图图层删除了
// @ts-ignore
const controls = this.map._controls;
for(let k = controls.length - 1; k >= 0; k--) {
if (controls[k] instanceof vjmap.Draw.Tool) {
this.map.removeControl(controls[k])
}
}
let sources = this.map.getStyle().sources;
for(let item in sources) {
if (item != "highlight-source" && sources[item].type == "geojson")
{
// 只是把类型为geojson的移除了。同时点击高亮的图层不要删除了
this.map.removeSourceEx(item);
}
}
}
if (options.clearAllData === true) {
if (this.map.hasVectorLayer()) {
this.map.disableVectorLayerHoverHighlight()
} else {
this.map.disableLayerClickHighlight();
}
// 把所有绘图图层删除了
// @ts-ignore
const controls = this.map._controls;
for(let k = controls.length - 1; k >= 0; k--) {
if (controls[k] instanceof vjmap.Draw.Tool) {
this.map.removeControl(controls[k])
}
}
let sources = this.map.getStyle().sources;
for(let item in sources) {
// @ts-ignore
this.map.removeSourceEx(item);
}
this.map.removeMarkers();
this.map.removePopups();
}
}
/**
* 移动图层
* @param {string} layerName 图层名称,参数必填
* @param {object} options 其他参数,参数可选
* @param {string} options.beforeLayerName 移动到此图层之前,参数可选
* @param {string} options.topMost 移动到最顶层,参数可选
* @param {string} options.bottomMost 移动到底层,参数可选
* @example
* // 示例一
* // 移动图层名称为layer1的数据图层到最顶层
* let layerName = 'layer1';
* this.moveLayer(layerName, {
* topMost: true
* });
* // 示例二
* // 移动图层名称为layer1的数据图层到最底层
* this.moveLayer(layerName, {
* bottomMost: true
* });
* // 示例三
* // 移动图层名称为layer1的数据图层到图层名称为layer2的数据图层之前
* this.moveLayer(layerName, {
* beforeLayerName: 'layer2'
* });
*/
moveLayer(layerName, options) {
let item = this.context.global.layers.find(layer => layer.layerName == layerName);
if(item) {
item.visible = true;
let layerId = item.layerId;
if (item.layer && typeof(item.layer.getLayerId) == "function") {
layerId = item.layer.getLayerId();
}
let layers = this.map.getStyle().layers;
let oldBeforeLayerId;
let index = layers.findIndex(item => item.id == layerId);
if (index < layers.length - 1) {
oldBeforeLayerId = layers[index + 1].id;
}
if (options.topMost) {
this.map.moveLayer(layerId);
} else if (options.bottomMost) {
this.map.moveLayer(layerId, layers[0].id);
} else if (options.beforeLayerName) {
let itemBefore = this.context.global.layers.find(layer => layer.layerName == options.beforeLayerName);
let beforeLayerId;
if (itemBefore) {
beforeLayerId = itemBefore.layerId;
if (itemBefore.layer && typeof(itemBefore.layer.getLayerId) == "function") {
beforeLayerId = itemBefore.layer.getLayerId();
}
}
this.map.moveLayer(beforeLayerId);
}
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
if (oldBeforeLayerId) {
this.map.moveLayer(layerId, oldBeforeLayerId);
}
});
}
}
/**
* 手动交互式绘制一条线
* @param {object} options 配置参数, 参数可选
* @param {boolean} options.isSnap 是否启用捕捉功能, 参数可选,默认false
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {number} options.width 线宽,默认1,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 手动绘制一条线
* const line = await this.drawLineString({
* isSnap: true,
* color: "#FF0000",
* width: 2
* });
* if (!line) return;
* console.log("线ID:", line.id);
* console.log("线的点坐标:", line.points);
*/
async drawLineString(options) {
let points = await this.actionDrawLineString(options.isSnap);
if (!points) return;
return this.createLineString(points, options);
}
/**
* 获取地图的最大、最小缩放级别
* @returns {number} 当前地图的最大缩放级别
*/
getMaxZoom() { this.map.getMaxZoom() }
/**
* 移除地图全屏控件
* @example
* // 移除地图全屏控件
* this.removeMapFullscreenControl();
*/
removeMapFullscreenControl() {
this.context.global.controls = this.context.global.controls || {};
let options;
if (this.context.global.controls.fullscreenControl) {
options = {...this.context.global.controls.fullscreenControl.options}
this.map.removeControl(this.context.global.controls.fullscreenControl.control);
delete this.context.global.controls.fullscreenControl;
}
// 增加撤销记录
this.context.ui.addUndoRecord(() => {
if (options) {
this.addMapFullscreenControl(options);
}
});
}
/**
* 弹出消息确认框
* @param {string} message 对话框内容,参数可选
* @param {string} title 对话框标题,参数可选
* @param {string} type 对话框类型,可选值为"warning"、"error"、"info"、"success",参数可选
* @returns {boolean} 返回用户点击的按钮, 点击"确定"返回true,点击"取消"返回false
* @example
* // 弹出消息确认框, 消息为"是否删除"
* let res = await this.messageBoxConfirm("是否删除", "提示", "warning");
* if (res) {
* console.log("点击了确定")
* } else {
* console.log("点击了取消")
* }
*/
async messageBoxConfirm(message, title, type) {
return await this.context.ui.confirm(message || "是否确认?", title || "提示", type || "warning");
}
/**
* 获取地图的旋转角度
* @returns {number} 当前地图的旋转角度
*/
getBearing() { this.map.getBearing() }
/**
* 获取当前地图服务版本号和运行环境信息
* @example
* // 获取当前地图服务版本号和运行环境信息, 获取返回数据弹出数据信息框,标题为地图服务版本信息,以表单格式显示
* let res = await this.getMapServerInfo();
* if (res) {
* await this.showDataInfoDialog(res, true, "地图服务版本信息");
* }
*/
async getMapServerInfo() {
let svc = this.map.getService();
const _url = svc.baseUrl() + "version";
let res = await svc._get(_url, {});
if (res && "data" in res) {
let data = res["data"];
return {
version: data.version,
os: data.os,
arch: data.arch
};
}
}
/**
* 设置当前地图栅格底图样式
* @param {object} options 其他参数,参数可选
* @param {number} options.rasterOpacity 图片的不透明度,参数可选,取值范围为 0 ~ 1,默认值为 1
* @param {number} options.rasterHueRotate 在色轮上旋转色相的角度,参数可选,默认值为 0,单位:角度
* @param {number} options.rasterBrightnessMin 图片的最小亮度,参数可选,取值范围为 0 ~ 1,默认值为 0
* @param {number} options.rasterBrightnessMax 图片的最大亮度,参数可选,取值范围为 0 ~ 1,默认值为 1
* @param {number} options.rasterSaturation 图片的饱和度,参数可选,取值范围为 -1 ~ 1,默认值为 0
* @param {number} options.rasterContrast 图片的对比度,参数可选,取值范围为 -1 ~ 1,默认值为 0
* @param {boolean} options.showSettingForm 是否弹出设置对话框. 默认true
* @example
* // 设置当前地图栅格底图样式
* this.setMapRasterLayerStyle();
*/
async setMapRasterLayerStyle(options) {
options = options || {};
let showSettingForm = options.showSettingForm === undefined ? true : options.showSettingForm;
let oldStyle = {...this.map.getLayerStyle(this.map.getService().rasterLayerId())?.paint};
// 将样式转换为驼峰命名
let formattedOldStyle = {};
for (let key in oldStyle) {
let camelCaseKey = key.replace(/-([a-z])/g, (match, p1) => p1.toUpperCase());
formattedOldStyle[camelCaseKey] = oldStyle[key];
}
oldStyle = formattedOldStyle;
delete options.showSettingForm;
options = {
rasterOpacity: oldStyle.rasterOpacity || 1,
rasterHueRotate: oldStyle.rasterHueRotate || 0,
rasterBrightnessMin: oldStyle.rasterBrightnessMin || 0,
rasterBrightnessMax: oldStyle.rasterBrightnessMax || 1,
rasterSaturation: oldStyle.rasterSaturation || 0,
rasterContrast: oldStyle.rasterContrast || 0,
...options,
}
let svc = this.map.getService();
let layerId = svc.rasterLayerId();
const setRasterStyle = (data) => {
this.map.setRasterOpacity(layerId, data.rasterOpacity || 1);
this.map.setRasterHueRotate(layerId, data.rasterHueRotate || 0);
this.map.setRasterBrightnessMin(layerId, data.rasterBrightnessMin || 0);
this.map.setRasterBrightnessMax(layerId, data.rasterBrightnessMax || 1);
this.map.setRasterSaturation(layerId, data.rasterSaturation || 0);
this.map.setRasterContrast(layerId, data.rasterContrast || 0);
}
if (showSettingForm) {
let dataOptions = {
rasterOpacity: {
type:"slider",
field:"rasterOpacity",
title:"图片的不透明度",
value:options.rasterOpacity,
props:{
min: 0,
max: 1,
step: 0.01
}
},
rasterHueRotate: {
type:"slider",
field:"rasterHueRotate",
title:"在色轮上旋转色相的角度",
value: options.rasterHueRotate,
props:{
min: 0,
max: 360
}
},
rasterBrightnessMin: {
type:"slider",
field:"rasterBrightnessMin",
title:"图片的最小亮度",
value: options.rasterBrightnessMin,
props:{
min: 0,
max: 1,
step: 0.01
}
},
rasterBrightnessMax: {
type:"slider",
field:"rasterBrightnessMax",
title:"图片的最大亮度",
value: options.rasterBrightnessMax,
props:{
min: 0,
max: 1,
step: 0.01
}
},
rasterSaturation: {
type:"slider",
field:"rasterSaturation",
title:"图片的饱和度",
value: options.rasterSaturation,
props:{
min: -1,
max: 1,
step: 0.01
}
},
rasterContrast: {
type:"slider",
field:"rasterContrast",
title:"图片的对比度",
value: options.rasterContrast,
props:{
min: -1,
max: 1,
step: 0.01
}
},
}
options = await this.showDataModifyDlg({
data: dataOptions,
title: "栅格底图样式设置",
showForm: true,
applyCallback: (data) => {
setRasterStyle(data);
}
})
if (!options) {
// 如果用户点击取消,则恢复原来的样式
setRasterStyle(oldStyle);
return
}
}
setRasterStyle(options);
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
setRasterStyle(oldStyle);
});
}
/**
* 设置地图的缩放级别
* @param {number} zoom 要设置的缩放级别
* @example
* this.setZoom(10); // 设置缩放级别为10
* // 将级别放大一级
* let zoom = this.getZoom(); // 获取原来的级别
* zoom = zoom + 1; // 放大一级
* this.setZoom(zoom);
*/
setZoom(zoom) {
let oldZoom = this.map.getZoom(); // 获取当前缩放级别
this.map.setZoom(zoom) // 设置新的缩放级别
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.setZoom(oldZoom);
});
}
/**
* 测量坐标
* @example
* // 测量坐标
* await this.measureCoordinate();
*/
measureCoordinate() {
vjcommon.runMeasureCmd(this.map, "measureCoordinate");
}
/**
* 根据数据创建等值线
* @param {Object} options 配置参数
* @param {number[]} options.breaks 等值线分级
* @param {string} [options.model] 插值模型,可选值为 'exponential', 'gaussian', 'spherical', 默认'exponential',
* @param {number} [options.sigma2] 方差参数,默认0, 一般设置为 0 ,其他数值设了可能会出空白图
* @param {number} [options.alpha] 方差函数的先验值,默认100,此参数可能控制钻孔扩散范围,越小范围越大,少量点效果明显,但点多了且分布均匀以后改变该数字即基本无效果了
* @param {string[]} [options.colors] 等值面分级区间颜色
* @param {number} [options.width] 生成等值线宽度参数,默认500
* @param {number} [options.contoursSize] 等值面分级区间数,默认20,值越大,等值线越密
* @param {[number, number, number, number]} [options.extent] 要绘制的范围,默认根据数据范围自动生成
* @param {number} [options.maxHeight] 最大值要拉伸的高度,默认100
* @param {[number, number]} [options.clipBounds] 裁剪范围,默认不裁剪
* @param {boolean} [options.drawSymbols] 是否绘制原始点,默认false
* @param {boolean} [options.drawPolyline] 是否绘制等值线,默认true
* @param {boolean} [options.drawPolygon] 是否绘制等值面,默认false
* @param {boolean} [options.drawFillExtrusion] 是否绘制拉伸等值面,默认false
* @returns {Object} 返回对象,包含创建数据源Id数组和图层Id数组 {sourceIds: string[], layerIds: string[]}
* @example
* // 在地图的实际范围内随机生成50个点,每个点随机赋值一个10到150的随机值,根据这些点绘制等值线
* let points = this.randomPointInMapBounds(50);
* let data = points.map(p => ({x: p[0], y: p[1], value: Math.floor(Math.random() * 90) + 10}));
* let contour = await this.vectorContour({data});
* console.log(contour);
*/
async vectorContour(options) {
options = options || {};
options.data = options.data || [];
let dataset = {
"type" : "FeatureCollection",
"features" : []
};
let dataMinValue = 0;
let dataMaxValue = 0;
options.data = options.data.map(item => {
return Array.isArray(item) ? {
x: item[0],
y: item[1],
value: item[2]
} : item;
});
let minx, miny, maxx, maxy;
for (let i = 0; i < options.data.length; i++) {
if (i == 0) {
dataMinValue = options.data[i].value;
dataMaxValue = options.data[i].value;
minx = options.data[i].x;
miny = options.data[i].y;
maxx = options.data[i].x;
maxy = options.data[i].y;
} else {
if (options.data[i].value < dataMinValue) dataMinValue = options.data[i].value;
if (options.data[i].value > dataMaxValue) dataMaxValue = options.data[i].value;
if (options.data[i].x < minx) minx = options.data[i].x;
if (options.data[i].y < miny) miny = options.data[i].y;
if (options.data[i].x > maxx) maxx = options.data[i].x;
if (options.data[i].y > maxy) maxy = options.data[i].y;
}
let feature={
"type" : "Feature",
"properties" : {
"value" : options.data[i].value
},
"geometry" : {
"type" : "Point",
"coordinates" : this.map.toLngLat([options.data[i].x,options.data[i].y])
}
};
dataset.features.push(feature);
}
let extent = options.extent || vjmap.GeoBounds.fromArray([minx, miny, maxx, maxy]).scale(1.2).toArray();
let contoursSize = options.contoursSize || 20; // 【值越大,等值线越密】等值面分级区间数,这里设置为20,可以自行设置
let model = options.model || 'exponential'; // 当前模型方法名
let alpha = options.alpha || 100; // 当前alpha值
let colors = options.colors || ["#006837", "#1a9850", "#66bd63", "#a6d96a", "#d9ef8b", "#ffffbf","#fee08b",
"#fdae61", "#f46d43", "#d73027", "#a50026"];
let propField = "value";
let maxHeight = options.maxHeight || this.map.pixelToHeight(100, this.map.getZoom()) ; // 设置最大值要拉伸的高度
let contours = [];
let isClipContour = false;
if (options.clipBounds) {
isClipContour = true;
options.clipBounds = vjmap.GeoBounds.fromArray(options.clipBounds);
}
for(let i = 0; i < contoursSize; i++) {
contours.push(dataMinValue + (dataMaxValue - dataMinValue) * i / (contoursSize - 1));
}
if (dataMinValue >= 10 && dataMaxValue > 100) {
// 等值线的值按10取整
for(let i = 0; i < contours.length; i++) {
contours[i] = (parseInt(contours[i]/10) * 10);
}
}
let interpolateInput = [], interpolateOutput = [];
for(let i = 0; i < colors.length; i++) {
interpolateInput.push(i / (colors.length - 1)); // 插值输入值,这里输入0-1之间的比例
interpolateOutput.push(colors[i]) // 插值输出值,这里输入0-1之间的比例对应的颜色值
}
// 启动webworker计算函数
let createContourWorker = vjmap.WorkerProxy(vjmap.vectorContour);
let { grid, contour, variogram } = await createContourWorker(this.map.fromLngLat(dataset), propField, contours, {
model: model || 'exponential', // 'exponential','gaussian','spherical',三选一,默认exponential
sigma2: options.sigma2 || 0, // sigma2是σ²,对应高斯过程的方差参数,也就是这组数据z的距离,方差参数σ²的似然性反映了高斯过程中的误差,并应手动设置。一般设置为 0 ,其他数值设了可能会出空白图
alpha: alpha, // [如果绘制不出来,修改此值,可以把此值改小] Alpha α对应方差函数的先验值,此参数可能控制钻孔扩散范围,越小范围越大,少量点效果明显,但点多了且分布均匀以后改变该数字即基本无效果了,默认设置为100
extent: extent, // 如果要根据数据范围自动生成此范围,则无需传此参数
width: options.width || 500 // 生成等值线宽度参数。像素长度。默认200。宽度值越大,绘制越精确,但也会导致速度变慢,内存占用越多
}, []);
contour = this.map.toLngLat(contour);
// 根据比例插值颜色
const mapProgressToValues = value => vjmap.interpolate(
interpolateInput,
interpolateOutput,
{ ease: vjmap.linear }
)(value)
// 把原数据的颜色也设置下,绘制marker需要
dataset.features.forEach(f => f.properties.color = mapProgressToValues((f.properties.value - dataMinValue) / (dataMaxValue - dataMinValue)))
let h = maxHeight; // 设置最大值要拉伸的高度
for(let i = 0; i < contour.features.length; i++) {
let prop = contour.features[i].properties;
let r = (prop.value - dataMinValue) / (dataMaxValue - dataMinValue);
prop.color = mapProgressToValues(r); // 插值出颜色值
prop.height = h * r; // 插值出要拉伸的高度值
}
contour.features.forEach(f => {
f.properties.formatValue = Math.round(f.properties.value) // 多加一个用于格式化的注记的值
})
let markers = null;
let markerLayerId = "marker_contour_" + vjmap.RandomID();
const addMarkers = ()=> {
if (markers) return;
markers = dataset.features.map(f => {
if (isClipContour && !clipBounds.contains(this.map.fromLngLat(f.geometry.coordinates))) {
// 如果是裁剪等值线,则超出范围的marker不进行绘制
return null;
}
// 再随机生成不同样式的
let _marker = new vjmap.DiffusedApertureMarker({
lngLat: f.geometry.coordinates,
text: f.properties.value.toFixed(0)
}, {
// 可以给不同的属性,如宽度,颜色,字体
width: 10,
colors: [f.properties.color, vjmap.randomColor()],
textFontSize: 14,
textColor: f.properties.color
}).createMarker();
_marker.layerId = markerLayerId
_marker.addTo(this.map)
return _marker
})
}
const removeMarkers = ()=> {
if (!markers) return;
for(let i = markers.length - 1; i >= 0; i--) {
if (markers[i]) markers[i].remove();
}
markers = null;
}
let symbols = null;
const addSymbols = async ()=> {
if (symbols) return;
let contourData = contour;
if (isClipContour) contourData = clipContours(contour, clipBounds);
// 增加一个文字背景图片,背景色和地图背景色一样
await this.map.addImageData("imgMaskText", `<svg viewBox="0 0 1024 1024" width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0L0 1024 1024 1024 1024 0z" fill="#022B4F"></path>
</svg>`, 30, 15);
symbols = new vjmap.Symbol({
data: contourData,
iconImage: "imgMaskText",
symbolSpacing: 250, // 符号空格,默认250像素
textField: [
'format',
["to-string", ['round', ['get', 'value']]], // 把值取整显示,如果要小数点用这个 ['number-format',['get', 'value'], { 'min-fraction-digits': 1, 'max-fraction-digits': 1 }],
{}
],
textFont: ['Arial Unicode MS Regular'],
textSize: 14,
textColor: '#00ffff',
symbolPlacement: "line",
textAnchor: 'center',
textRotationAlignment: "this.map",
iconAllowOverlap: false,
textAllowOverlap: false
});
symbols.addTo(this.map);
}
const removeSymbols = ()=> {
if (!symbols) return;
symbols.remove();
symbols = null;
}
let polyline = null;
const addPolyline = ()=> {
if (polyline) return;
let contourData = contour;
if (isClipContour) contourData = clipContours(contour, clipBounds);
polyline = new vjmap.Polyline({
data: contourData,
lineColor: ['case', ['to-boolean', ['feature-state', 'hover']], '#00ffff', ['get', 'color']],
isHoverPointer: true,
isHoverFeatureState: true
});
polyline.addTo(this.map);
polyline.clickPopup(f => `<h3>值: ${f.properties.value.toFixed(2)}</h3>Color: ${f.properties.color}`, { anchor: 'bottom' });
}
const removePolyline = ()=> {
if (!polyline) return;
polyline.remove();
polyline = null;
removeSymbols();
}
const drawPolyline = async () => {
addPolyline();
removeSymbols();
await addSymbols();
}
let polygon = null;
const addPolygon = ()=> {
if (polygon) return;
let contourData = contour;
if (isClipContour) contourData = clipContoursPolygon(contour, clipBounds);
polygon = new vjmap.Polygon({
data: contourData,
fillColor: ['case', ['to-boolean', ['feature-state', 'hover']], '#00ffff', ['get', 'color']],
fillOpacity: 0.9,
isHoverPointer: true,
isHoverFeatureState: true
});
polygon.addTo(this.map);
polygon.clickPopup(f => `<h3>值: ${f.properties.value.toFixed(2)}</h3>Color: ${f.properties.color}`, { anchor: 'bottom' });
}
const removePolygon = ()=> {
if (!polygon) return;
polygon.remove();
polygon = null;
removeSymbols();
}
const drawPolygon = async () => {
addPolygon();
removeSymbols();
await addSymbols();
}
let fillExtrusions = null;
const addFillExtrusion = ()=> {
if (fillExtrusions) return;
fillExtrusions = new vjmap.FillExtrusion({
data: contour,
fillExtrusionColor: ['case', ['to-boolean', ['feature-state', 'hover']], '#00ffff', ['get', 'color']],
fillExtrusionOpacity: 0.9,
fillExtrusionHeight: ['get', 'height'],
fillExtrusionBase:0,
isHoverPointer: true,
isHoverFeatureState: true
});
fillExtrusions.addTo(this.map);
fillExtrusions.clickPopup(f => `<h3>值: ${f.properties.value.toFixed(2)}</h3>Color: ${f.properties.color}`, { anchor: 'bottom' });
}
const removeFillExtrusion = ()=> {
if (!fillExtrusions) return;
fillExtrusions.remove();
fillExtrusions = null;
removeSymbols();
}
const drawFillExtrusion = async ()=> {
addFillExtrusion();
removeSymbols();
await addSymbols();
}
let clipPolyline = null;
const addClipPolyline = () => {
if (clipPolyline) return;
let points = clipBounds.toPointArray();
points.push(points[0]);
clipPolyline = new vjmap.Polyline({
data: this.map.toLngLat(points),
lineWidth: 2,
lineColor: 'red'
});
clipPolyline.addTo(this.map);
}
const removeClipPolyline = ()=> {
if (!clipPolyline) return;
clipPolyline.remove();
clipPolyline = null;
}
// 要裁剪的多段线数组和要裁剪的范围
const clipContours = (polylineData, clipBounds) => {
let data = {
type: 'FeatureCollection',
features: []
}
for(let k = 0; k < polylineData.features.length; k++) {
// 遍历每一个子多边形
for(let n = 0; n < polylineData.features[k].geometry.coordinates.length; n++) {
let coordinates = this.map.fromLngLat(polylineData.features[k].geometry.coordinates[n]);
// 一段一段的与范围去裁剪
let clipCoordinates = [];
let clipSubCoordinates = [];
for(let j = 1; j < coordinates.length; j++) {
let p1 = vjmap.geoPoint(coordinates[j - 1]);
let p2 = vjmap.geoPoint(coordinates[j]);
let res = vjmap.clipSegment(p1, p2, clipBounds);
if (res === false) {
// 在范围外,之前的线段弄个单独一条线段,后面的重新做为一条新的线段
if (clipSubCoordinates.length > 0) {
clipCoordinates.push(this.map.toLngLat(clipSubCoordinates));
clipSubCoordinates = [];
}
continue;
}
let pt1 = [res[0].x, res[0].y];
let pt2 = [res[1].x, res[1].y];
clipSubCoordinates.push(pt1, pt2);
}
if (clipSubCoordinates.length > 0) {
clipCoordinates.push(this.map.toLngLat(clipSubCoordinates));
}
if (clipCoordinates.length > 0) {
for(let i = 0; i < clipCoordinates.length; i++) {
let feature = vjmap.cloneDeep(polylineData.features[k]);
feature.geometry.type = "LineString" ;// 由多边形改成多段线
feature.geometry.coordinates = clipCoordinates[i];
data.features.push(feature);
}
}
}
}
return data
}
// 要裁剪的多边形和要裁剪的范围
const clipContoursPolygon = (polygonData, clipBounds) => {
let data = {
type: 'FeatureCollection',
features: []
}
for (let k = 0; k < polygonData.features.length; k++) {
// 遍历每一个子多边形
for (let n = 0; n < polygonData.features[k].geometry.coordinates.length; n++) {
let feature = vjmap.cloneDeep(polygonData.features[k]);
feature.geometry.coordinates = []
let coordinates = this.map.fromLngLat(polygonData.features[k].geometry.coordinates[n]);
let clipCoordinates = vjmap.clipPolygon(coordinates, clipBounds)
if (clipCoordinates.length > 2) {
clipCoordinates.push(clipCoordinates[0]); //闭合
feature.geometry.coordinates.push(this.map.toLngLat(clipCoordinates));
feature.properties.area = vjmap.calcPolygonArea(clipCoordinates)
data.features.push(feature)
}
}
}
// 按面积大小排下序。有时候裁剪后会导致一些面积大的覆盖之前面积小的,导致小的看不见
data.features = data.features.sort((a, b) => b.properties.area - a.properties.area)
return data
}
if (options.drawSymbols) {
addMarkers();
}
if (options.drawPolyline !== false) {
await drawPolyline();
}
if (options.drawPolygon) {
await drawPolygon();
}
if (options.drawFillExtrusion) {
await drawFillExtrusion();
}
if (isClipContour) {
addClipPolyline();
}
let sourceIds = [];
let layerIds = [];
if (markers) {
sourceIds.push(markerLayerId);
layerIds.push(markerLayerId);
}
if (symbols) {
sourceIds.push(symbols.sourceId);
layerIds.push(symbols.layerId);
}
if (polyline) {
sourceIds.push(polyline.sourceId);
layerIds.push(polyline.layerId);
}
if (polygon) {
sourceIds.push(polygon.sourceId);
layerIds.push(polygon.layerId);
}
if (fillExtrusions) {
sourceIds.push(fillExtrusions.sourceId);
layerIds.push(fillExtrusions.layerId);
}
if (clipPolyline) {
sourceIds.push(clipPolyline.sourceId);
layerIds.push(clipPolyline.layerId);
}
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
for(let i = 0; i < sourceIds.length; i++) {
if (sourceIds[i].indexOf("marker_contour_") == 0) {
// 如果是marker, 需要单独处理下
let markers = this.map.getMarkers();
// 倒序删除
for(let j = markers.length - 1; j >= 0; j--) {
if (markers[j].layerId == sourceIds[i]) {
markers[j].remove();
}
}
} else {
this.map.removeSourceEx(sourceIds[i]);
}
}
});
return { sourceIds, layerIds }
}
/**
* 获取WMS瓦片URL
* @param {object} options WMS配置选项
* @param {string} [options.mapid] 地图ID
* @param {string} [options.version] 地图版本(为空时采用当前打开的地图版本)
* @param {string} [options.layer] 图层样式名称(如果为空时,则用默认样式)
* @param {string} [options.epsg] cad图epsg代号
* @param {"beijing54"|"xian80"|"cgs2000"} [options.crs] cad图的坐标系,beijing54(北京54坐标)、xian80(西安80坐标、cgs2000(CGCS2000坐标)
* @param {string} [options.webMapTiles] 叠加地图为互联网地图时,数据瓦片地址, 可为空
* @param {boolean} [options.overMapImage] 叠加地图为互联网地图时,是否为影像地图
* @param {boolean} [options.isSetRotateZero] 公共点叠加时是否允许旋转
* @param {boolean} [options.baseMapWgs84] 底图是否为互联网地图wgs84坐标
* @returns {Promise<string[]>} 返回wms瓦片URL数组
* @example
* // 叠加的地图mapid为sys_hello, 获取WMS瓦片URL, 根据此瓦片地址, 创建栅格瓦片图层
* let wmsTileUrl = await this.getWmsTileUrl({
* mapid: "sys_hello"
* });
* let layer = await this.createRasterLayer({
* tiles: wmsTileUrl,
* });
* console.log(layer);
*/
async getWmsTileUrl(options) {
options = options || {}
let svc = this.map.getService();
// 判断底图是否为互联网地图
let isWebMap = this.map.getMapProjection() instanceof vjmap.LnglatProjection;
let baseMapType = "CAD"
if (isWebMap) {
if (options.baseMapWgs84 === undefined) {
// 自动获取下
baseMapType = "GCJ02";
let sources = this.map.getStyle().sources;
for (let name in sources) {
let source = sources[name];
if (source.type === 'raster') {
let tiles = source.tiles;
if (tiles && tiles.length > 0) {
let tile = tiles[0];
if (tile.includes('tianditu') || tile.includes('openstreetmap')) {
baseMapType = "WGS84";
break;
}
}
}
}
} else {
baseMapType = options.baseMapWgs84 === true ? "WGS84" : "GCJ02";
}
}
let mapParam = svc.currentMapParam() || {}
let baseMapParam = {
// 底图类型
baseMapType: baseMapType,
// 地图ID
mapid: mapParam.mapid,
// 地图版本
version: mapParam.version,
// 地图范围
mapbounds: JSON.stringify(this.map.getGeoBounds(1.0).toArray())
}
let overlayType = options.overlayType;
if (overlayType !== "param") {
if (options.epsg !== undefined) {
overlayType = "auto";
} else {
let epsg = await this.getCadEpsgCode(options);
if (epsg) {
options.epsg = epsg;
overlayType = "auto";
}
}
}
if (overlayType === undefined && options.coordinates !== undefined) {
overlayType = "param";
}
let overMapType = options.overMapType || "CAD";
if (options.webMapTiles === undefined) {
let isImageUrl = options.overMapImage === true; // 是否为影像地图
if (overMapType === "WGS84") {
options.webMapTiles = vjcommon.tiandituProviderTiles(isImageUrl);
} else if (overMapType === "GCJ02") {
options.webMapTiles = vjcommon.gaodeProviderTiles(isImageUrl);
}
}
if (overlayType === "auto" && !options.epsg) {
this.showMessage("请设置CAD图的epsg参数");
return;
}
let overMapParam = {
// 地图ID(为空时采用当前打开的mapid)
mapid: options.mapid,
// 地图版本(为空时采用当前打开的地图版本)
version: options.version ,
// 图层样式名称
layer: options.layer,
// cad图的坐标系
crs: options.epsg,
// 叠加类型
baseMapType: overMapType,
// 底图为互联网地图时,数据瓦片地址
webMapTiles: Array.isArray(options.webMapTiles) ? options.webMapTiles : [options.webMapTiles] ,
isSetRotateZero: options.isSetRotateZero, // 公共点叠加时是否允许旋转
coordinates: options.coordinates // 公共点坐标 x1,y1底图坐标, x2,y2对应点坐标
};
return await vjcommon.getWmsTileUrl(svc, baseMapParam, overlayType || "direct", {maps: overMapParam})
}
/**
* 导出为新的CAD图形
*/
async exportCAD() {
this.map.logInfo("正在导出dwg并打开,请稍候...")
const res = await vjcommon.exportDwg(this.map, this.map.getDrawLayer());
if (res.error) {
this.map.logInfo(res.error, "error");
return;
}
const isCloud = this.map.getService().serverUrl.indexOf("vjmap.com") >= 0;
const host = isCloud ? "https://vjmap.com/app/cloud" : (window.location.origin + "/_cloud");
const url = host + `/#/this.map/${res.mapid}?mapopenway=Memory&version=${res.version}`;
window.open(url);
}
/**
* 创建路径动画数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {string} options.lineColor 线条颜色,默认#FF9900
* @param {number} options.lineWidth 线宽,默认10
* @param {number} options.lineOpacity 线透明度,取值0-1
* @param {number} options.lineGapWidth 线间隙宽度,在线的实际路径之外绘制描边时的内部间隙宽度
* @param {number} options.lineOffset 线的偏移量,正值向右偏移,负值向左偏移
* @param {number} options.lineBlur 线模糊度,需小于线宽度
* @param {string} options.strokeStyle 线型,'solid'实线或'dashed'虚线
* @param {[number,number]} options.lineDasharray 虚线样式,格式如[1,1]表示实线虚线的组合
* @param {[number,number]} options.lineTranslate 平移量,格式[x,y],负数分别表示左和上
* @param {string} options.lineCap 线帽形状,'butt'尖头、'round'圆头、'square'平头
* @param {string} options.lineJoin 拐角形状,'bevel'平拐、'round'圆拐、'miter'棱拐
* @param {string} options.strokeImage 线图案,需与预加载的图片名对应
* @param {string} options.lineGradient 线渐变
* @param {boolean} options.showBorder 是否显示线边框,默认true
* @param {string} options.borderColor 边框颜色,默认#f00
* @param {number} options.borderWidth 边框宽度
* @param {number} options.borderOpacity 边框透明度,取值0-1
* @param {number} options.borderGapWidth 边框间隙宽度
* @param {number} options.borderOffset 边框偏移量
* @param {number} options.borderBlur 边框模糊度
* @param {string} options.borderGradient 边框渐变
* @param {boolean} options.showDir 是否显示箭头,默认true
* @param {number} options.dirSize 箭头大小
* @param {number} options.dirSpacing 箭头间距
* @param {number} options.dirImageWidth 箭头图像宽度
* @param {number} options.dirImageHeight 箭头图像高度
* @param {string} options.dirImageColor 箭头图像颜色
* @param {string} options.dirIconColor 箭头图标颜色
* @param {string} options.dirLayout 箭头布局
* @param {string} options.cursor 鼠标光标形状
* @param {string} options.iconImage 图标名称,用于绘制图像背景的图像名称
* @param {number} options.iconRotate 图标旋转角度,顺时针旋转,单位为角度,默认0
* @param {[number,number]} options.iconOffset 图标与锚点偏移距离,[x,y]格式,正值向右和向下
* @param {string} options.iconAnchor 图标锚定位置,可选值: "center", "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right"
* @param {string} options.iconRotationAlignment 图标旋转对齐方式,可选值: "map", "viewport", "auto"
* @param {string} options.iconPitchAlignment 地图倾斜时图标方向,可选值: "map", "viewport", "auto"
* @param {boolean} options.iconAllowOverlap 是否允许图标重叠,默认false
* @param {number} options.iconOpacity 图标透明度,0-1之间,默认1
* @param {boolean} options.iconIgnorePlacement 是否忽略碰撞检测,默认false
* @param {boolean} options.iconOptional 图标重叠选项,默认false
* @param {number} options.iconSize 图标缩放比例,默认1
* @param {string} options.iconTextFit 图标适应文本方式,可选值: "none", "width", "height", "both"
* @param {[number,number,number,number]} options.iconTextFitPadding 图标文本匹配padding,[top, right, bottom, left]格式
* @param {number} options.iconPadding 图标边界填充大小,默认1像素
* @param {boolean} options.iconKeepUpright 是否防止图标倒置,默认false
* @param {string} options.iconColor SDF图标颜色,默认"#000000"
* @param {string} options.iconHaloColor SDF图标光晕颜色,默认"rgba(0, 0, 0, 0)"
* @param {number} options.iconHaloWidth 图标光晕宽度,默认0
* @param {number} options.iconHaloBlur 图标光晕模糊度,默认0
* @param {[number,number]} options.iconTranslate 图标平移距离,[x,y]格式
* @param {string} options.iconTranslateAnchor 图标平移参考系,可选值: "map", "viewport"
* @param {string|Expression} options.textField 文本标签内容
* @param {string[]} options.textFont 文本字体,默认["Open Sans Regular","Arial Unicode MS Regular"]
* @param {number} options.textSize 文本大小,默认16
* @param {boolean} options.textAllowOverlap 是否允许文本重叠,默认false
* @param {string} options.textColor 文本颜色,默认"#000000"
* @param {number} options.textOpacity 文本透明度,0-1之间,默认1
* @param {[number,number]} options.textOffset 文本偏移,[x,y]格式
* @param {number} options.textRotate 文本旋转角度
* @param {number} options.textPadding 文本内边距,默认2
* @param {boolean} options.textKeepUpright 是否防止文本倒置,默认true
* @param {number} options.textMaxWidth 文本最大宽度,默认10em
* @param {number} options.textLineHeight 文本行高,默认1.2
* @param {number} options.textLetterSpacing 文字间距,默认0
* @param {string} options.textJustify 文本对齐方式,可选值: "auto", "left", "center", "right"
* @param {string} options.textAnchor 文本锚点位置
* @param {string} options.textRotationAlignment 文本旋转对齐方式
* @param {string} options.textPitchAlignment 文本倾斜对齐方式
* @param {number} options.textMaxAngle 相邻字符最大角度,默认45°
* @param {string[]} options.textWritingMode 文本书写模式
* @param {string} options.textTransform 文本转换方式,可选值: "none", "uppercase", "lowercase"
* @param {number} options.textRadialOffset 文本径向偏移
* @param {string[]} options.textVariableAnchor 文本可变锚点位置数组
* @param {boolean} options.textIgnorePlacement 是否忽略文本位置
* @param {boolean} options.textOptional 文本显示选项
* @param {string} options.textHaloColor 文本光晕颜色
* @param {number} options.textHaloWidth 文本光晕宽度
* @param {number} options.textHaloBlur 文本光晕模糊度
* @param {[number,number]} options.textTranslate 文本平移距离
* @param {string} options.textTranslateAnchor 文本平移参考系
* @param {string} options.symbolPlacement 符号放置位置,可选值: "point", "line", "line-center"
* @param {number} options.symbolSpacing 符号间距,默认250
* @param {boolean} options.symbolAvoidEdges 是否避免边缘碰撞
* @param {string} options.symbolZOrder 符号绘制顺序,可选值: "auto", "viewport-y", "source"
* @param {number} options.fps 动画帧率,每秒运行多少帧,默认10,把线段分成了100段,每一秒跑多少段,取值范围1-100
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer21的路径动画数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createPathAnimateLayer(geojson, {
* layerName: 'layer21',
* iconImage: 'icon1'
* });
* console.log(layer);
*/
async createPathAnimateLayer(data, options) {
options = options || {};
let type = "pathAnimate";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
lineWidth: 10,
showDir: true,
showBorder: true,
borderColor: "#f00",
lineColor: '#FF9900',
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createPathAnimateLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 创建旋转的文本框数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {string} options.text 要显示的文字,参数可选,默认为"index ${props.index}"
* @param {number} options.minZoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxZoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isDrawLinePoint 绘制点时包括线上所有点, 参数可选,默认为false
* @param {number} options.width 像素宽度,参数可选,默认为24
* @param {number} options.markerHeight 像素高度,参数可选
* @param {string} options.color1 颜色值一,参数可选
* @param {string} options.color2 颜色值二,参数可选
* @param {number} options.textFontSize 字体大小,参数可选
* @param {string} options.textColor 字体颜色,参数可选
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {string} options.animationType 动画类型,参数可选,无动画 MAP_ANIMATION_NONE,弹跳 MAP_ANIMATION_BOUNCE, 坠落 MAP_ANIMATION_DROP
* @param {number} options.rotation 旋转角度,参数可选
* @param {string} options.rotationAlignment 旋转对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {string} options.pitchAlignment 倾斜对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {number} options.height 坐标标高值,参数可选
* @param {number} options.scaleMaxZoom 能缩放的最大级别,参数可选
* @param {boolean} options.removeWhenNoInMapView 视图范围内自动显隐,参数可选
* @param {number} options.removeWhenNoInMapViewPadding 视图范围内自动显隐Padding像素范围,参数可选,默认500
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer28的旋转的文本框数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createTextBorderMarkerLayer(geojson, {
* layerName: 'layer28'
* });
* console.log(layer);
*/
async createTextBorderMarkerLayer(data, options) {
options = options || {};
let type = "textBorderMarker";
if (options.height) {
options.height = this.map.geoLengthToHeight(options.height);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
text: "ID:${props.index}",
width: 40,
markerHeight: 20,
color1: "#ff0000",
color2: "#ffff00",
textFontSize: 15,
textColor: "#00ffff",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createTextBorderMarkerLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 创建符号路径动画数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {string} options.iconImage 图标名称,用于绘制图像背景的图像名称
* @param {number} options.iconRotate 图标旋转角度,顺时针旋转,单位为角度,默认0
* @param {[number,number]} options.iconOffset 图标与锚点偏移距离,[x,y]格式,正值向右和向下
* @param {string} options.iconAnchor 图标锚定位置,可选值: "center", "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right"
* @param {string} options.iconRotationAlignment 图标旋转对齐方式,可选值: "map", "viewport", "auto"
* @param {string} options.iconPitchAlignment 地图倾斜时图标方向,可选值: "map", "viewport", "auto"
* @param {boolean} options.iconAllowOverlap 是否允许图标重叠,默认false
* @param {number} options.iconOpacity 图标透明度,0-1之间,默认1
* @param {boolean} options.iconIgnorePlacement 是否忽略碰撞检测,默认false
* @param {boolean} options.iconOptional 图标重叠选项,默认false
* @param {number} options.iconSize 图标缩放比例,默认1
* @param {string} options.iconTextFit 图标适应文本方式,可选值: "none", "width", "height", "both"
* @param {[number,number,number,number]} options.iconTextFitPadding 图标文本匹配padding,[top, right, bottom, left]格式
* @param {number} options.iconPadding 图标边界填充大小,默认1像素
* @param {boolean} options.iconKeepUpright 是否防止图标倒置,默认false
* @param {string} options.iconColor SDF图标颜色,默认"#000000"
* @param {string} options.iconHaloColor SDF图标光晕颜色,默认"rgba(0, 0, 0, 0)"
* @param {number} options.iconHaloWidth 图标光晕宽度,默认0
* @param {number} options.iconHaloBlur 图标光晕模糊度,默认0
* @param {[number,number]} options.iconTranslate 图标平移距离,[x,y]格式
* @param {string} options.iconTranslateAnchor 图标平移参考系,可选值: "map", "viewport"
* @param {string|Expression} options.textField 文本标签内容
* @param {string[]} options.textFont 文本字体,默认["Open Sans Regular","Arial Unicode MS Regular"]
* @param {number} options.textSize 文本大小,默认16
* @param {boolean} options.textAllowOverlap 是否允许文本重叠,默认false
* @param {string} options.textColor 文本颜色,默认"#000000"
* @param {number} options.textOpacity 文本透明度,0-1之间,默认1
* @param {[number,number]} options.textOffset 文本偏移,[x,y]格式
* @param {number} options.textRotate 文本旋转角度
* @param {number} options.textPadding 文本内边距,默认2
* @param {boolean} options.textKeepUpright 是否防止文本倒置,默认true
* @param {number} options.textMaxWidth 文本最大宽度,默认10em
* @param {number} options.textLineHeight 文本行高,默认1.2
* @param {number} options.textLetterSpacing 文字间距,默认0
* @param {string} options.textJustify 文本对齐方式,可选值: "auto", "left", "center", "right"
* @param {string} options.textAnchor 文本锚点位置
* @param {string} options.textRotationAlignment 文本旋转对齐方式
* @param {string} options.textPitchAlignment 文本倾斜对齐方式
* @param {number} options.textMaxAngle 相邻字符最大角度,默认45°
* @param {string[]} options.textWritingMode 文本书写模式
* @param {string} options.textTransform 文本转换方式,可选值: "none", "uppercase", "lowercase"
* @param {number} options.textRadialOffset 文本径向偏移
* @param {string[]} options.textVariableAnchor 文本可变锚点位置数组
* @param {boolean} options.textIgnorePlacement 是否忽略文本位置
* @param {boolean} options.textOptional 文本显示选项
* @param {string} options.textHaloColor 文本光晕颜色
* @param {number} options.textHaloWidth 文本光晕宽度
* @param {number} options.textHaloBlur 文本光晕模糊度
* @param {[number,number]} options.textTranslate 文本平移距离
* @param {string} options.textTranslateAnchor 文本平移参考系
* @param {string} options.symbolPlacement 符号放置位置,可选值: "point", "line", "line-center"
* @param {number} options.symbolSpacing 符号间距,默认250
* @param {boolean} options.symbolAvoidEdges 是否避免边缘碰撞
* @param {string} options.symbolZOrder 符号绘制顺序,可选值: "auto", "viewport-y", "source"
* @param {string} options.lineColor 线条颜色,参数可选,默认为'#ff0000'
* @param {number} options.lineWidth 线宽,参数可选,默认为1
* @param {number} options.lineOpacity 线透明度,参数可选,取值范围0-1
* @param {number} options.lineGapWidth 线间隙宽度,参数可选,默认为1,单位像素
* @param {number} options.lineOffset 线的偏移量,参数可选。正值向右/内偏移,负值向左/外偏移
* @param {number} options.lineBlur 线模糊度,参数可选,需小于线宽度
* @param {[number,number]} options.lineDasharray 虚线样式,参数可选,格式如[1,1]表示实线虚线的组合
* @param {[number,number]} options.lineTranslate 平移量,参数可选,格式如[x,y],负数表示左和上,单位像素
* @param {string} options.lineCap 线帽形状,参数可选,可选值:'butt'(尖头)、'round'(圆头)、'square'(平头)
* @param {string} options.lineJoin 拐角形状,参数可选,可选值:'bevel'(平拐)、'round'(圆拐)、'miter'(棱拐)
* @param {string} options.linePattern 线图案,参数可选,需与预加载的图片名称对应,图像宽度必须是2的幂次
* @param {string} options.lineGradient 线渐变,参数可选
* @param {boolean} options.drawPath 是否绘制路径线,可选值: true, false
* @param {number} options.anmiDuration 动画持续时长,单位为秒,默认5。时长为0表示没有动画
* @param {number} options.anmiRepeat 动画持续次数,默认0。次数为0表示无限循环
* @param {string} options.anmiRepeatType 动画重复类型,可选值: "reverse"(来回往返), "loop"(循环), "mirror"(镜像)
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer26的符号路径动画数据图层, 图标名称为_markerIcon_
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createSymbolPathAnimateLayer(geojson, {
* layerName: 'layer26',
* iconImage: '_markerIcon_'
* });
* console.log(layer);
*/
async createSymbolPathAnimateLayer(data, options) {
options = options || {};
let type = "symbolPathAnimate";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
drawPath: true,
anmiDuration: 5,
lineColor: '#75E7EF',
lineOpacity: 0.4,
lineWidth: 20,
iconAllowOverlap: true,
textAllowOverlap: true,
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createSymbolPathAnimateLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 查询全部块实体
* @param {string | string[]} blockName 要查询的块名称或块名称数组,参数可选,默认为空,为空时查询全部块
* @returns {Object} 返回标准的GeoJson格式的FeatureCollection对象, 包含features属性, 每个feature包含geometry和properties属性,properties属性中包含如下字段:
* @example
* // 查询全部块实体
* let geoJson = await this.queryMapAllBlock();
* // 获取结果
* console.log(geoJson)
*/
async queryMapAllBlock(blockName) {
let svc = this.map.getService();
let query = await svc.exprQueryFeature({
// 先表达式查询所有的块实体,因为如何用条件查询的话,块已经转为了离散的空间数据,不能整体查了,所以先通过表达式查询出块信息
/// 以下是查所有块。如果要查询某个具体的块名可以这样写 expr: `gOutReturn := if((gInFeatureType == 'AcDbBlockReference' and gInAttrs4 == '您要查询的块名称'), 1, 0);`,
expr: `gOutReturn := if((gInFeatureType == 'AcDbBlockReference'), 1, 0);`,
fields: "objectid,blockname,layername,positon,rotate,scaleX,scaleY,scaleZ,bounds", // 为空的话,表达所有字段
geom: false, // 内存模式
useCache: true,
limit: 1000000 // 数量大些,相当于查找所有的
})
let blockNameSet = new Set();
if (Array.isArray(blockName)) {
blockName.forEach(name => {
blockNameSet.add(name)
})
} else if (typeof blockName === "string") {
blockNameSet.add(blockName)
}
let geoJson = {
type: "FeatureCollection",
features: []
}
if (query.error) {
this.map.logInfo(query.error, "error")
return geoJson
}
for(let i = 0; i < query.result.length; i++) {
if (blockNameSet.size > 0 && !blockNameSet.has(query.result[i].blockname)) {
continue
}
query.result[i].bounds = vjmap.GeoBounds.fromString(query.result[i].bounds).toArray();
let feature = {
type: "Feature",
geometry: {
type: "Point",
coordinates: vjmap.GeoPoint.fromString(query.result[i].positon).toArray()
},
properties: query.result[i]
}
geoJson.features.push(feature)
}
return geoJson
}
/**
* 获取地图范围
* @param {number} 范围类型(0 地图绘制范围,1 地图实际范围, 2 地图整个范围),默认0, 参数可选
* @returns {[number, number, number, number]} 返回包含四个数字的数组[x1,y1,x2,y2],表示地图范围的左下角和右上角坐标
* @example
* // 获取地图的范围
* let rect = this.getMapGeoBounds();
* let minx = rect[0];
* let miny = rect[1];
* let maxx = rect[2];
* let maxy = rect[3];
* let width = maxx - minx; // 地图范围宽度
* let height = maxy - miny; // 地图范围高度
* console.log(minx, miny, maxx, maxy, width, height);
* // 获取地图实际范围
* let rect2 = this.getMapGeoBounds(1);
* console.log(rect2);
*/
getMapGeoBounds(boundsType) {
boundsType = boundsType || 0;
let geoBounds;
if (boundsType == 1) {
// CAD图实际范围(整个地图范围的0.4倍) this.map.getGeoBounds(0.4) // 正方形
geoBounds = this.map.getGeoBounds(0.4)
} else if (boundsType == 2) {
// 整个地图范围 this.map.getGeoBounds(1.0) // 正方形
geoBounds = this.map.getGeoBounds(1)
} else {
// 地图绘制范围
geoBounds = this.map.getService().currentMapParam().drawBounds || this.map.getGeoBounds(0.4)
}
return geoBounds.toArray()
}
/**
* 创建文本数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {string} options.text 要显示的文字,参数可选,默认为"index ${props.index}"
* @param {Object} options.style 文本样式设置,参数可选
* @param {number} options.minZoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxZoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isDrawLinePoint 绘制点时包括线上所有点, 参数可选,默认为false
* @param {string} options.color 标记颜色,参数可选,默认为'#3FB1CE'
* @param {number[]} options.offset 偏离XY像素值,如[0,0],负数表示向左和向上
* @param {number} options.rotation 标记旋转角度,顺时针旋转,默认0
* @param {string} options.anchor 标记锚点位置,可选值:center、top、bottom、left、right、top-left、top-right、bottom-left、bottom-right,默认center
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {string} options.animationType 动画类型:MAP_ANIMATION_NONE(无动画)、MAP_ANIMATION_BOUNCE(弹跳)、MAP_ANIMATION_DROP(坠落)
* @param {string} options.rotationAlignment 旋转对齐:auto(自动)、map(相对地图)、viewport(相对视口),默认auto
* @param {string} options.pitchAlignment 倾斜对齐:auto(自动)、map(相对地图)、viewport(相对视口),默认auto
* @param {number} options.scale 缩放比例,默认1,对应高度41px宽度27px
* @param {number} options.scaleMaxZoom 能缩放的最大级别(0-24),小于此级别时自动缩放
* @param {number} options.height 标高值,单位CAD图制图单位
* @param {boolean} options.removeWhenNoInMapView 是否在视图范围内自动显隐
* @param {number} options.removeWhenNoInMapViewPadding 视图范围内自动显隐的padding像素值,默认500
* @param {number} options.clickTolerance 点击像素容差,默认继承地图的clickTolerance
* @param {string} options.customImage 自定义图片地址/base64/svg内容
* @param {number} options.customImageWidth 自定义图片宽度
* @param {number} options.customImageHeight 自定义图片高度
* @param {string} options.customHtml 自定义Html元素的JS代码
* @param {string} options.popupHtml 信息窗口HTML内容
* @param {[number,number]} options.popupOffset 信息窗口偏离XY像素值
* @param {string} options.popupAnchor 信息窗口锚点位置,同anchor参数
* @param {boolean} options.closeButton 是否显示关闭按钮
* @param {boolean} options.closeOnClick 是否在点击地图时关闭弹出窗口
* @param {boolean} options.closeOnMove 是否在地图移动后关闭信息窗口
* @param {boolean} options.focusAfterOpen 是否尝试聚焦弹出窗口内第一个可聚焦元素
* @param {string} options.className 添加到弹出容器的CSS类名
* @param {string} options.maxWidth 弹出窗口最大宽度,CSS属性字符串,默认'240px'
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer27的文本数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createTextDataLayer(geojson, {
* layerName: 'layer27'
* });
* console.log(layer);
*/
async createTextDataLayer(data, options) {
options = options || {};
let type = "text";
if (options.height) {
options.height = this.map.geoLengthToHeight(options.height);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
text: "ID:${props.index}",
style: {
'cursor': 'pointer',
'opacity': 1,
'padding': '12px',
'border-radius': '4px',
'background-color': '#0ff',
'border-width': 0,
'box-shadow': '0px 2px 6px 0px rgba(97,113,166,0.2)',
'text-align': 'center',
'font-size': '14px',
'color': '#F33'
},
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createTextDataLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 创建曲线数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {string} options.lineColor 线条颜色,参数可选,默认为'#ff0000'
* @param {number} options.lineWidth 线宽,参数可选,默认为1
* @param {number} options.lineOpacity 线透明度,参数可选,取值范围0-1
* @param {number} options.lineGapWidth 线间隙宽度,参数可选,默认为1,单位像素
* @param {number} options.lineOffset 线的偏移量,参数可选。正值向右/内偏移,负值向左/外偏移
* @param {number} options.lineBlur 线模糊度,参数可选,需小于线宽度
* @param {[number,number]} options.lineDasharray 虚线样式,参数可选,格式如[1,1]表示实线虚线的组合
* @param {[number,number]} options.lineTranslate 平移量,参数可选,格式如[x,y],负数表示左和上,单位像素
* @param {string} options.lineCap 线帽形状,参数可选,可选值:'butt'(尖头)、'round'(圆头)、'square'(平头)
* @param {string} options.lineJoin 拐角形状,参数可选,可选值:'bevel'(平拐)、'round'(圆拐)、'miter'(棱拐)
* @param {string} options.linePattern 线图案,参数可选,需与预加载的图片名称对应,图像宽度必须是2的幂次
* @param {string} options.lineGradient 线渐变,参数可选
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer8的曲线数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createCurveLayer(geojson, {
* layerName: 'layer8'
* });
* console.log(layer);
*/
async createCurveLayer(data, options) {
options = options || {};
let type = "curve";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
lineColor: '#00ffff',
lineWidth: 3,
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createCurveLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 测量角度
* @example
* // 测量角度
* await this.measureAngle();
*/
measureAngle() {
vjcommon.runMeasureCmd(this.map, "measureAngle");
}
/**
* 对地图重命名
* @param {string} oldMapID 旧地图ID
* @param {string} newMapID 新地图ID
* @returns {Promise<boolean>} 返回重命名结果true表示成功,false表示失败
* @example
* // 将地图ID从old_hello重命名为new_hello
* let ok = await this.renameMap("old_hello", "new_hello");
* console.log(ok);
*/
async renameMap(oldMapID, newMapID) {
let svc = this.map.getService();
let res = await svc.cmdRenameMap(oldMapID, newMapID);
return res && res.status;
}
/**
* 叠加互联网地图
* @param {object} options 叠加配置选项
* @param {string} [options.epsg] cad图epsg代号
* @param {"beijing54"|"xian80"|"cgs2000"|""} [options.crs] cad图的坐标系,beijing54(北京54坐标)、xian80(西安80坐标、cgs2000(CGCS2000坐标) 或 空
* @param {number} [options.webmaptype] 叠加的互联网地图类型,1:高德地图,2:高德影像,3:天地图,4:天地图影像
* @returns {Promise<string>} 返回叠加的图层名称
* @example
* // 示例一
* // 叠加互联网地图,cad图的坐标系为2000坐标
* let layerName = await this.overlayWebMap({
* crs: "cgs2000"
* });
* console.log("layerName", layerName);
* // 示例二
* // 叠加天地图影像地图,cad图的坐标系为西安80坐标
* let layerName2 = await this.overlayWebMap({
* crs: "xian80",
* webmaptype: 4
* });
* console.log("layerName", layerName2);
* // 示例三
* // 叠加互联网地图
* let layerName3 = await this.overlayWebMap();
* console.log("layerName", layerName3);
*/
async overlayWebMap(options) {
options = {...options}
// 判断底图是否为互联网地图
let isWebMap = this.map.getMapProjection() instanceof vjmap.LnglatProjection;
if (isWebMap) {
this.showMessage("当前地图已经是互联网地图,无法叠加");
return;
}
let epsg = options.epsg;
if (!epsg) {
epsg = await this.getCadEpsgCode(options);
}
let webmaptype = options.webmaptype ?? 1;
let fp;
if (!epsg) {
// 用四参数叠加, 左边是默认地图,右边是互联网地图
fp = await this.context.ui.getFourParamter({}, {webmaptype: webmaptype});
if (!fp) return;
if (fp.webmaptype == 1 || fp.webmaptype == 2) {
// 高德地图
options.overMapType = "GCJ02";
if (fp.webmaptype == 2) {
// 影像
options.overMapImage = true;
}
} else if (fp.webmaptype == 3 || fp.webmaptype == 4) {
// 天地图
options.overMapType = "WGS84";
if (fp.webmaptype == 4) {
// 影像
options.overMapImage = true;
}
}
options.isSetRotateZero = !fp.allowsRotation;
options.coordinates = [];
for(let n = 0; n < fp.leftMarkers.length; n++) {
let leftMarker = fp.leftMarkers[n];
let rightMarker = fp.rightMarkers[n];
options.coordinates.push({
x1: rightMarker.x,
y1: rightMarker.y,
x2: leftMarker.x,
y2: leftMarker.y
});
}
options.overlayType = "param";
} else {
options.overlayType = "auto";
if (webmaptype == 1 || webmaptype == 2) {
// 高德地图
options.overMapType = "GCJ02";
if (webmaptype == 2) {
// 影像
options.overMapImage = true;
}
} else if (webmaptype == 3 || webmaptype == 4) {
// 天地图
options.overMapType = "WGS84";
if (webmaptype == 4) {
// 影像
options.overMapImage = true;
}
}
}
let wmsTileUrl;
if (options.overlayType == "param") {
// 四参数叠加
let b = this.map.getMapExtent();
if (options.overMapType == "WGS84") {
// 天地图
let webMapTiles = vjcommon.tiandituProviderTiles(options.overMapImage);
const tiles = webMapTiles.map(t => vjcommon.getTileShards(t)) || [];
wmsTileUrl = this.map.getService().webMapUrl({
tileCrs: "wgs84",
tileUrl: tiles?.map(t => t.tileUrl) ?? '',
tileShards: (tiles.length >= 0) ? tiles[0].tileShards : '',
tileToken: "",
tileSize: 256,
tileRetina: 1,
tileMaxZoom: 18,
tileFlipY: false,
// @ts-ignore
mapbounds: JSON.stringify([b.min.x,b.min.y,b.max.x,b.max.y]),
srs: "EPSG:3857",
fourParameterBefore: fp.fourParamter
})
} else {
// 高德地图
let webMapTiles = vjcommon.gaodeProviderTiles(options.overMapImage);
const tiles = webMapTiles.map(t => vjcommon.getTileShards(t)) || [];
wmsTileUrl = this.map.getService().webMapUrl({
tileCrs: "gcj02",
tileUrl: tiles?.map(t => t.tileUrl) ?? '',
tileShards: (tiles.length >= 0) ? tiles[0].tileShards : '',
tileToken: "",
tileSize: 256,
tileRetina: 1,
// @ts-ignore
tileMaxZoom: 18,
tileFlipY: false,
mapbounds: JSON.stringify([b.min.x,b.min.y,b.max.x,b.max.y]),
srs: "EPSG:3857",
fourParameterBefore: fp.fourParamter
})
}
} else {
// 自动叠加
wmsTileUrl = await this.getWmsTileUrl({
epsg: epsg,
overMapType: options.overMapType,
overMapImage: options.overMapImage,
isSetRotateZero: options.isSetRotateZero,
coordinates: options.coordinates,
overlayType: options.overlayType
});
}
if (!wmsTileUrl) return;
// 创建栅格瓦片图层
let layer = await this.createRasterLayer({
tiles: wmsTileUrl,
});
// 把图层移动到最下面
this.moveLayer(layer.layerName, {
bottomMost: true
});
return layer
}
/**
* 设置CAD图实体对象的隐藏或显示
* @param {Array<string>} objectids 实体id数组
* @param {boolean} visible 是否可见, true表示可见,false表示不可见
*/
async setCadEntityVisible(objectids, visible) {
if (!this.context.global.updateMapStyleObj) {
this.context.global.updateMapStyleObj = vjcommon.createUpdateMapStyleObj(this.map);
}
const updateMapStyleObj = this.context.global.updateMapStyleObj;
if (visible) {
await updateMapStyleObj.removeHideObjectIds(new Set(objectids));
} else {
await updateMapStyleObj.addHideObjectIds(new Set(objectids));
}
// 添加撤销记录
this.context.ui.addUndoRecord(async () => {
if (!visible) {
await updateMapStyleObj.removeHideObjectIds(new Set(objectids));
} else {
await updateMapStyleObj.addHideObjectIds(new Set(objectids));
}
});
}
/**
* 切换图纸工作区
* @param {string} workspaceName 工作区名称, 可选,为空时则选择工作区
* @example
* // 切换图纸工作区
* this.switchWorkspace();
*/
async switchWorkspace(workspaceName) {
// 先获取默认的图层
let svc = this.map.getService();
let oldWorkspaceName = svc.getCurWorkspaceName();
if (!workspaceName) {
let workspaces = await svc.getWorkspaces() || [];
let workspaceNames = workspaces.map(wp => wp.name + (wp.alias ? "[" + wp.alias + "]" : ""));
let oldWorkspace = workspaces.find(wp => wp.name === oldWorkspaceName);
if (oldWorkspace) {
oldWorkspaceName = oldWorkspace.name + (oldWorkspace.alias ? "[" + oldWorkspace.alias + "]" : "");
}
workspaceNames.unshift("[默认工作区]");
workspaceName = await this.selectOptionsValue(workspaceNames, {
isMultiSelect: false,
title: "选择工作区",
initValue: oldWorkspaceName || "[默认工作区]"
});
if (!workspaceName) {
return;
}
if (workspaceName) {
// 取前面的名称
workspaceName = workspaceName.split("[")[0];
}
}
svc.switchWorkspace(workspaceName);
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
svc.switchWorkspace(oldWorkspaceName);
});
}
/**
* 对坐标数据进行坐标转换
* @param {string} srs 源坐标系名称,如 EPSG:4326
* @param {string} crs 目标坐标系名称,如 EPSG:3857
* @param {[number, number] | [number, number][]} points 要转换的坐标
* @param {string | string[]} [fourParameter] 四参数(x偏移,y偏移,缩放,旋转弧度),可选,对坐标最后进行修正
* @param {boolean} [isInverseFourParamter] 是否是上面四参数坐标的反算
* @returns {Promise<[number, number] | [number, number][]>} 返回转换后的坐标
* @example
* // 对坐标数据进行坐标转换,将坐标[120.0, 30.0]从EPSG:4326转换到EPSG:3857
* let transformedPoints = await this.coordTransform("EPSG:4326", "EPSG:3857", [120.0, 30.0]);
* console.log(transformedPoints);
*/
async coordTransform(srs, crs, points, fourParameter, isInverseFourParamter) {
let svc = this.map.getService();
let isOnePoint = false;
if (Array.isArray(points)) {
if (points.length > 0 && !Array.isArray(points[0])) {
isOnePoint = true;
points = vjmap.geoPoint(points);
} else {
points = points.map((point) => vjmap.geoPoint(point));
}
}
let result = await svc.cmdTransform(srs, crs, points, fourParameter, isInverseFourParamter);
if (result && result.error) {
if (this.map.logInfo) this.map.logInfo(result.error, "error", 10000);
return;
}
if (isOnePoint) {
return result[0];
}
return result;
}
/**
* 获取绘制的全部图元
* @returns {<GeoJSON | undefined>} 返回选中的GeoJSON对象
* @example
* // 获取绘制的全部图元的图元ID
* const geojson = this.getDrawAll();
* if (!geojson) return;
* let ids = [];
* for(let i = 0; i < geojson.features.length; i++) {
* let feature = geojson.features[i];
* ids.push(feature.id);
* }
* console.log(ids);
*/
getDrawAll() {
let draw = this.map.getDrawLayer();
return this.map.fromLngLat(draw.getAll());
}
/**
* 在地图范围内随机生成GeoJson数据
* @param {Record<string, any>} options 参数,参数可选
* @param {number} boundsType 范围类型(0 地图绘制范围,1 地图实际范围, 2 地图整个范围),默认0, 参数可选
* @param {string} options.type 类型,值为点 'point',线 'line',面 'polygon', 所有 'all',默认'all'
* @param {number} options.pointCount 最多点数,默认10
* @param {number} options.lineCount 最多线数,默认10
* @param {number} options.polygonCount 最多面数,默认10
* @param {number} options.linePointCount 每条线上最多点数,默认3
* @param {number} options.polygonPointCount 每个面最多点数,默认3
* @returns {Object} 返回GeoJson数据,包含features属性, 每个feature包含geometry和properties属性,properties属性中包含index字段
* @example
* // 在地图范围内随机生成GeoJson数据
* let geojson = this.randomGeojsonInMapBounds();
* console.log(geojson);
* // 在地图范围内随机生成GeoJson数据,类型为点,最多生成50个点
* let ptGeoJson = this.randomGeojsonInMapBounds({
* type: 'point',
* pointCount: 50
* });
* console.log(ptGeoJson);
*/
randomGeojsonInMapBounds(options) {
options = options || {};
options.type = options.type || 'all';
let rect = this.getMapGeoBounds(options.boundsType || 0);
let geoBounds = vjmap.GeoBounds.fromArray(rect);
let FeatureCollection = {
type: "FeatureCollection",
features: []
};
if (options.type == 'point' || options.type == 'all') {
let coll = geoBounds.randomGeoJsonPointCollection(Math.max(options.pointCount || 10, 2));
if (options.pointCount == 1) {
// @ts-ignore
FeatureCollection.features.push(coll.features[0]);
} else {
// @ts-ignore
FeatureCollection.features.push(...coll.features);
}
}
if (options.type == 'line' || options.type == 'all') {
let coll = geoBounds.randomGeoJsonLineCollection(options.lineCount || 10, options.linePointCount || 3);
// @ts-ignore
FeatureCollection.features.push(...coll.features);
}
if (options.type == 'polygon' || options.type == 'all') {
let coll = geoBounds.randomGeoJsonPolygonCollection(options.polygonCount || 10, options.polygonPointCount || 3);
// @ts-ignore
FeatureCollection.features.push(...coll.features);
}
for(let i = 0; i < FeatureCollection.features.length; i++) {
let feature = FeatureCollection.features[i];
feature.id = i;
feature.properties = {
index: i
}
}
return FeatureCollection;
}
/**
* 测量距离
* @example
* // 测量距离
* await this.measureDistance();
*/
measureDistance() {
vjcommon.runMeasureCmd(this.map, "measureDist");
}
/**
* 创建动画符号数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {string} options.animateImagesType 动画类型,可选值:"arrow"(箭头动画), "antpath"(蚂蚁线动画), "customImage"(自定义图片), "imageSprites"(同一个图片精灵动画集)
* @param {string} options.animateImages_arrowFillColor 箭头填充颜色,当animateImagesType为"arrow"时有效,默认"#22B14C"
* @param {string} options.animateImages_arrowStrokeColor 箭头边框颜色,当animateImagesType为"arrow"时有效,默认"#fff"
* @param {number} options.animateImages_arrowStrokeWidth 箭头线宽,当animateImagesType为"arrow"时有效
* @param {number} options.animateImages_arrowWidth 箭头大小,当animateImagesType为"arrow"时有效
* @param {string} options.animateImages_fillColor1 蚂蚁线填充颜色1,当animateImagesType为"antpath"时有效
* @param {string} options.animateImages_fillColor2 蚂蚁线填充颜色2,当animateImagesType为"antpath"时有效
* @param {string} options.customImageUrl 自定义图片URL或base64,当animateImagesType为"customImage"时有效
* @param {string} options.imageSpriteUrl 图片动画集URL或base64,当animateImagesType为"imageSprites"时有效
* @param {number} options.animateImages_spriteWidth 来源图片中精灵图所占宽度,当animateImagesType为"imageSprites"时有效
* @param {number} options.animateImages_spriteHeight 来源图片中精灵图所占高度,当animateImagesType为"imageSprites"时有效
* @param {number} options.animateImages_canvasWidth 画布宽度,除animateImages类型外的所有动画类型通用
* @param {number} options.animateImages_canvasHeight 画布高度,除animateImages类型外的所有动画类型通用
* @param {number} options.animateImages_frameCount 动画帧数,除animateImages类型外的所有动画类型通用
* @param {boolean} options.animateImages_directionReverse 动画方向是否反向,除animateImages类型外的所有动画类型通用
* @param {number} options.speed 动画速度,步长0.1
* @param {boolean} options.startAutoAnimation 是否开始时自动播放动画
* @param {string} options.symbolPlacement 符号放置位置,可选值:"point"(几何图形点上),"line"(几何图形直线上),"line-center"(几何图形线中心)
* @param {number} options.symbolSpacing 两个符号中心点之间的距离,单位像素,默认250
* @param {boolean} options.symbolAvoidEdges 符号是否避免碰撞,默认false
* @param {string} options.symbolZOrder 重叠符号顺序,可选值:"auto","viewport-y","source"
* @param {string} options.iconImage 图标名称,用于绘制图像背景
* @param {number} options.iconRotate 图标旋转角度,默认0
* @param {[number, number]} options.iconOffset 图标与锚点偏移距离,默认[0,0]
* @param {string} options.iconAnchor 图标锚定位置,可选值:"center","left","right","top","bottom","top-left","top-right","bottom-left","bottom-right"
* @param {string} options.iconRotationAlignment 图标旋转对齐,可选值:"map","viewport","auto"
* @param {string} options.iconPitchAlignment 地图倾斜时图标方向,可选值:"map","viewport","auto"
* @param {boolean} options.iconAllowOverlap 图标是否允许重叠,默认false
* @param {number} options.iconOpacity 图标透明度,0-1之间,默认1
* @param {boolean} options.iconIgnorePlacement 图标是否忽略碰撞,默认false
* @param {boolean} options.iconOptional 图标重叠选项,默认false
* @param {number} options.iconSize 图标缩放大小,默认1
* @param {string} options.iconTextFit 图标适应文本,可选值:"none","width","height","both"
* @param {[number,number,number,number]} options.iconTextFitPadding 图标文本匹配padding,默认[0,0,0,0]
* @param {number} options.iconPadding 图标边界填充大小,默认1
* @param {boolean} options.iconKeepUpright 图标是否防止颠倒呈现,默认false
* @param {string} options.iconColor SDF图标颜色,默认"#000000"
* @param {string} options.iconHaloColor SDF图标光圈颜色,默认"rgba(0,0,0,0)"
* @param {number} options.iconHaloWidth 光圈到图标轮廓距离,默认0
* @param {number} options.iconHaloBlur 图标光晕模糊宽度,默认0
* @param {[number,number]} options.iconTranslate 图标平移距离,默认[0,0]
* @param {string} options.iconTranslateAnchor 图标平移锚点,可选值:"map","viewport"
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer4的动画符号数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createAnimateSymbolLayer(geojson, {
* layerName: 'layer4'
* });
* console.log(layer);
*/
async createAnimateSymbolLayer(data, options) {
options = options || {};
let type = "animateSymbol";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
animateImagesType: "arrow",
animateImages_arrowFillColor: "#22B14C",
animateImages_arrowStrokeColor: "#fff",
animateImages_arrowStrokeWidth: 6,
animateImages_canvasWidth: 128,
animateImages_canvasHeight: 32,
animateImages_arrowWidth: 16,
animateImages_frameCount: 4,
animateImages_fillColor1: "#f0fb",
animateImages_fillColor2: "#0ffb",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createAnimateSymbolLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 创建信息窗口数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {string} options.text 要显示的文字,参数可选,默认为"index ${props.index}"
* @param {number} options.minZoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxZoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isDrawLinePoint 绘制点时包括线上所有点, 参数可选,默认为false
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {string} options.html 信息窗口的HTML内容,支持props内置属性变量
* @param {[number, number]} options.offset 以像素为单位的偏移量[x,y],对象相对于元素的中心。负数表示向左和向上,默认[0,0]
* @param {string} options.anchor 信息窗口锚点位置,可选值:'center'、'top'、'bottom'、'left'、'right'、'top-left'、'top-right'、'bottom-left'、'bottom-right',默认'center'
* @param {boolean} options.closeButton 是否显示关闭按钮,参数可选
* @param {boolean} options.closeOnClick 是否在单击地图时关闭弹出窗口,参数可选
* @param {boolean} options.closeOnMove 是否在地图移动后关闭信息窗口,参数可选
* @param {boolean} options.focusAfterOpen 是否尝试聚焦弹出窗口内的第一个可聚焦元素,参数可选
* @param {string} options.className 添加到弹出容器的以空格分隔的CSS类名,参数可选
* @param {string} options.maxWidth 设置弹出窗口最大宽度的CSS属性字符串,如'300px'。设为'none'时自适应内容,默认'240px'
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer22的信息窗口数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createPopupDataLayer(geojson, {
* layerName: 'layer22'
* });
* console.log(layer);
*/
async createPopupDataLayer(data, options) {
options = options || {};
let type = "popup";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
color: '#3FB1CE',
html: "<span style='color:#0000ff'>index:${props.index}</span>",
closeButton: true,
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createPopupDataLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 增加鼠标位置控件
* @param {number} options.digits 小数点位数,参数可选
* @param {boolean} options.trackCenter 是否跟踪中心点位置,参数可选
* @param {boolean} options.showLatLng 是否显示经纬度,参数可选
* @param {boolean} options.showZoom 是否显示缩放级别,参数可选
* @param {boolean} options.showBearing 是否显示方位角,参数可选
* @param {boolean} options.showPitch 是否显示倾斜角,参数可选
* @param {'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'} options.position 控件位置,参数可选, 默认'top-right'
* @example
* // 增加鼠标位置控件,位置右下角
* this.addMousePositionControl({ position: 'bottom-right' });
*/
addMousePositionControl(options) {
this.context.global.controls = this.context.global.controls || {};
if (this.context.global.controls.mousePositionControl) {
this.map.removeControl(this.context.global.controls.mousePositionControl.control);
}
let mousePositionControl = new vjmap.MousePositionControl(options);
this.map.addControl(mousePositionControl, options.position);
this.context.global.controls.mousePositionControl = {
control: mousePositionControl,
position: options.position,
options: options
};
// 增加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.removeControl(mousePositionControl);
delete this.context.global.controls.mousePositionControl;
});
}
/**
* 弹出列表选择框
* @param {Array<string>} values 可选值列表内容,必填
* @param {Object} options 可选值列表配置,可选
* @param {string|string[]} options.initValue 初始值,可选,默认空
* @param {boolean} options.isMultiSelect 是否多选,可选,默认false
* @param {string} options.title 对话框标题,可选
* @param {Function} options.applyCallback 点击应用按钮的回调函数,可选
* @returns {string|string[]|undefined} 返回用户选择的值, 如果用户点击取消,则返回undefined
* @example
* // 示例一
* // 弹出列表选择框,列表内容为["a","b","c"]
* let res = await this.selectOptionsValue(["a","b","c"]);
* if (res) {
* // 如果单选,则返回字符串
* console.log("选择的字符串值为:", res)
* } else {
* console.log("点击了取消")
* }
* // 示例二
* // 弹出列表选择框,列表内容为["a","b","c","d"],多选, 标题为"选择", 初始值为["a","b"]
* let arr = await this.selectOptionsValue(["a","b","c","d"], {
* isMultiSelect: true,
* title: "选择",
* initValue: ["a","b"]
* });
* if (arr) {
* // 如果多选,则返回数组
* console.log("选择的数组值为:", arr)
* } else {
* console.log("点击了取消")
* }
*/
selectOptionsValue(values, options) {
options = options || {};
return this.context.ui.selectOptionsValue(values, {
isMultiSelect: options.isMultiSelect || false,
initValue: options.initValue || [],
title: options.title || "选择",
applyCallback: options.applyCallback || undefined
});
}
/**
* 手动交互式绘制一个点符号
* @param {object} options 配置参数, 参数可选
* @param {boolean} options.isSnap 是否启用捕捉功能, 参数可选,默认false
* @param {string} options.icon 符号图标名称,默认为_markerIcon_,参数可选
* @param {number} options.iconSize 符号图标放大倍数,默认为1,参数可选
* @param {number} options.iconRotate 符号图标旋转角度,默认为0,参数可选
* @param {string} options.text 符号文本内容,默认为空,参数可选
* @param {string} options.textColor 符号文本颜色,默认为空,参数可选
* @param {number} options.textSize 符号文本大小,默认为22,参数可选
* @param {number} options.textRotate 符号文本旋转角度,默认为0,参数可选
* @param {number} options.textOpacity 符号文本透明度,默认为0,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 示例一
* // 手动绘制一个点符号
* const pt = await this.drawSymbol(); // 启用捕捉功能采绘制一个点符号
* if (!pt) return;
* // 点的坐标
* let point = pt.point;
* const x = point[0];
* const y = point[1];
* console.log(x, y);
* // 图元ID
* let id = pt.id;
* console.log(id);
* // 示例二
* // 手动绘制一个点符号,符号图标名称为_pulsingDot_
* const pt2 = await this.drawSymbol({
* icon: "_pulsingDot_"
* });
* console.log(pt2);
* // 示例三
* // 手动绘制一个点符号,符号图标名称为空,文本内容为"Hello",文本旋转角度为45
* const pt3 = await this.drawSymbol({
* text: "Hello",
* textRotate: 45
* });
* console.log(pt3);
*/
async drawSymbol(options) {
options = options || {};
let point = await this.actionDrawPoint(options.isSnap);
if (!point) return;
return this.createSymbol(point, options)
}
/**
* 手动交互式绘制一个斜矩形
* @param {object} options 配置参数, 参数可选
* @param {boolean} options.isSnap 是否启用捕捉功能, 参数可选,默认false
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {boolean} options.isFill 是否填充,默认false,参数可选
* @param {number} options.opacity 透明度,默认1,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 手动绘制一个斜矩形
* const rect = await this.drawSlantRectangle({
* isSnap: true,
* color: "#FF0000",
* isFill: true,
* opacity: 0.8
* });
* if (!rect) return;
* console.log("斜矩形ID:", rect.id);
* console.log("斜矩形的点坐标:", rect.points);
*/
async drawSlantRectangle(options) {
let points = await this.actionDrawSlantRectangle(options.isSnap);
if (!points) return;
return this.createPolygon(points, options);
}
/**
* 增加小地图(鹰眼)控件
* @param {'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'} options.position 控件位置,参数可选, 默认'top-right'
* @example
* // 增加小地图(鹰眼)控件,位置左下角
* this.addMiniMapControl({ position: 'bottom-left' });
*/
addMiniMapControl(options) {
this.context.global.controls = this.context.global.controls || {};
if (this.context.global.controls.miniMapControl) {
this.map.removeControl(this.context.global.controls.miniMapControl.control);
}
let transformOrigin = "100% 0%";
if (options.position === 'top-left') {
transformOrigin = "0% 0%";
} else if (options.position === 'bottom-right') {
transformOrigin = "100% 100%";
} else if (options.position === 'bottom-left') {
transformOrigin = "0% 100%";
}
let miniMapControl = new vjmap.MiniMapControl({
containerStyle: {
background: "#31346a85",
transform: "scale(0.6)",
transformOrigin: transformOrigin
},
...options
});
this.map.addControl(miniMapControl, options.position);
this.context.global.controls.miniMapControl = {
control: miniMapControl,
position: options.position,
options: options
};
// 增加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.removeControl(miniMapControl);
delete this.context.global.controls.miniMapControl;
});
}
/**
* 设置地图的旋转角度
* @param {number} bearing 要设置的旋转角度
* @example
* this.setBearing(45); // 设置旋转角度为45度
*/
setBearing(bearing) {
let oldBearing = this.map.getBearing(); // 获取当前旋转角度
this.map.setBearing(bearing); // 设置新的旋转角度
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.setBearing(oldBearing);
});
}
/**
* 根据根据的坐标位置直接绘制一个点符号
* @param {[number, number]} point 点符号的坐标[x, y],参数必填
* @param {object} options 配置参数, 参数可选
* @param {boolean} options.isSnap 是否启用捕捉功能, 参数可选,默认false
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {number} options.size 点大小,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 示例一
* // 在位置[10, 20]处绘制一个点
* const pt = this.createPoint([10, 20]); // 启用捕捉功能采绘制一个点坐标
* if (!pt) return;
* // 点的坐标
* let point = pt.point;
* const x = point[0];
* const y = point[1];
* console.log(x, y);
* // 图元ID
* let id = pt.id;
* console.log(id);
* // 示例二
* // 在地图范围内随机生成1个点, 在这个点绘制一个点,颜色为红色,大小为10,数据值为123
* let points = this.randomPointInMapBounds(1);
* const pt2 = this.createPoint(points[0], {
* color: "#FF0000",
* size: 10,
* data: 123
* });
* console.log(pt2);
*/
createPoint(point, options) {
options = options || {};
let draw = this.map.getDrawLayer();
let id = vjmap.RandomID(10)
draw.add({
id,
type: 'Feature',
geometry: {
type: 'Point',
coordinates: this.map.toLngLat(point)
},
properties: {
"color_static": options.color,
"radius_static": options.size,
"data": options.data
}
});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.getDrawLayer().delete(id);
});
return {
id,
point
};
}
/**
* 设置信息窗口Popup的属性
* @param {string} id 信息窗口Popup的id
* @param {Record<string, any>} options 其他参数,参数可选
* @param {[number, number]} options.position 坐标[x, y],参数可选,默认为undefined
* @param {any} options.content 弹窗内容,参数可选,默认为空表示不显示弹窗
* @param {boolean} options.showInForm 弹窗内容是否以表单形式显示,参数可 选,默认为false
* @param {number} options.elevation 坐标标高值,参数可选,默认为0
* @param {boolean} options.visible 是否显示,参数可选,默认为true
* @param {any} options.data 数据内容,参数可选,默认为空
* @example
* // 设置id为popup_1的信息窗口的属性,坐标为[10, 20],弹窗内容为{name: "Sensor", value: 100}, 弹窗内容以表单形式展示
* this.setPopupProperty("popup_1", {
* position: [10, 20],
* content: {name: "Sensor", value: 100},
* showInForm: true
* });
*/
setPopupProperty(id, options) {
let popups = this.context.global.popups || [];
let item = popups.find(m => m.id == id);
if (!item) return;
let popup = item.popup;
let oldOptions = {...item.options, ...options};
if (options.position) {
popup.setLngLat(this.map.toLngLat(options.position));
}
if (options.data !== undefined) {
popup.data = options.data;
}
if (options.content !== undefined || options.showInForm !== undefined) {
const dom = this.context.ui.createElementByJsonData(options.content || oldOptions.content, options.showInForm || oldOptions.showInForm)
popup.setDOMContent(dom.element)
}
if (options.elevation !== undefined) {
popup.setHeight(options.elevation);
}
if (options.data !== undefined) {
popup.data = options.data;
}
if (options.visible !== undefined) {
if (options.visible) {
if (!popup._map) {
popup.addTo(this.map);
}
} else {
if (popup._map) {
popup.remove();
}
}
}
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.setPopupProperty(id, oldOptions);
});
}
/**
* 清除所有选择的实体对象
* @example
* // 清除所有选择的实体对象
* this.clearSelectedFeatures();
*/
clearSelectedFeatures() {
vjcommon.clearHighlight(this.map);
}
/**
* 设置数据图层点击显示弹窗内容
* @param {string} layerName 图层名称,参数必填
* @param {Record<string, any>} options 其他参数,参数可选
* @param {any} options.popupContent 弹窗内容,默认为当前数据的属性值
* @param {boolean} options.popupShowInForm 弹窗内容是否以表单形式显示,参数可选,默认为false
* @param {[number, number]} options.popupOffset 弹窗偏移量,参数可选,默认为[0, -40]
* @param {number} options.popupWidth 弹窗宽度,参数可选
* @param {number} options.popupHeight 弹窗高度,参数可选
* @param {boolean} options.noShowPopup 是否不显示弹窗,参数可选,默认为false
* @param {Function} options.clickCallback 点击回调函数,参数可选,默认为null(回调函数第一个参数为弹窗内容)
* @example
* // 示例一
* // 设置图层名称为layer1的数据图层点击显示弹窗, 以表单形式展示
* this.setDataLayerClickPopupContent('layer1', {
* popupShowInForm: true
* });
* // 示例二
* // 设置图层名称为layer2的数据图层点击显示弹窗, 弹窗内容为"ID:${props.index}"
* this.setDataLayerClickPopupContent('layer2', {
* popupContent: "ID:${props.index}",
* });
*/
setDataLayerClickPopupContent(layerName, options) {
let item = this.context.global.layers.find(layer => layer.layerName == layerName);
if(!item) return;
options = options || {};
options.isHoverPointer = options.isHoverPointer === undefined ? true : options.isHoverPointer;
let markers = item.layer.markers;
if (Array.isArray(markers)) {
// 如果是Marker相关的图层
for(let m = 0; m < markers.length; m++) {
let marker = markers[m];
marker.on('click', () => {
let popup = this.context.global.popup;
if (!popup && options.noShowPopup !== true) {
popup = new vjmap.Popup({
offset: options.popupOffset || [0, -40],
closeOnClick: false,
closeButton: true,
anchor: "bottom"
})
this.context.global.popup = popup;
}
if (options.noShowPopup !== true && options.popupOffset) {
popup.setOffset(options.popupOffset);
}
let content = options.popupContent;
if (item.data && item.data.features && item.data.features[m] && item.data.features[m].properties && typeof content == "string" && !(content in item.data.features[m].properties)) {
content = item.layer.evalValue("return `" + content + "`", item.data.features[m].properties, this.map);
if (options.noShowPopup !== true) {
popup.setHTML(content);
}
if (options.clickCallback) {
options.clickCallback(content);
}
} else {
if (!content) {
content = item.data.features[m].properties;
} else {
// 如果content是对象的属性,则获取对象的属性值
if (item.data.features[m].properties && (content in item.data.features[m].properties)) {
content = item.data.features[m].properties[content];
try {
// 如果content是json字符串,则尝试转换为对象
content = JSON.parse(content);
} catch (e) {}
}
}
if (options.noShowPopup !== true) {
const dom = this.context.ui.createElementByJsonData(content, options.popupShowInForm, {
width: options.popupWidth,
height: options.popupHeight
})
popup.setDOMContent(dom.element)
}
if (options.clickCallback) {
options.clickCallback(content);
}
}
if (options.noShowPopup !== true) {
popup.setLngLat(marker.getLngLat());
popup.addTo(this.map);
}
});
}
} else {
// 如果是其他图层
let layerId = item.layerId;
if (item.layer && typeof(item.layer.getLayerId) == "function") {
layerId = item.layer.getLayerId();
}
this.map.on("click", layerId, (ev) => {
if (!ev || !ev.features || !ev.features[0]) return;
let pt = ev.lngLat;
let popup = this.context.global.popup;
if (!popup && options.noShowPopup !== true) {
popup = new vjmap.Popup({
offset: options.popupOffset || [0, -40]
})
this.context.global.popup = popup;
}
if (options.noShowPopup !== true && options.popupOffset) {
popup.setOffset(options.popupOffset);
}
let content = options.popupContent;
if (ev.features[0].properties && typeof content == "string" && !(content in ev.features[0].properties)) {
content = item.layer.evalValue("return `" + content + "`", ev.features[0].properties, this.map);
if (options.noShowPopup !== true) {
popup.setHTML(content);
}
if (options.clickCallback) {
options.clickCallback(content);
}
} else {
ev.features[0].properties = ev.features[0].properties || {}
if (!content) {
content = ev.features[0].properties;
} else {
// 如果content是对象的属性,则获取对象的属性值
if (content in ev.features[0].properties) {
content = ev.features[0].properties[content];
try {
// 如果content是json字符串,则尝试转换为对象
content = JSON.parse(content);
} catch (e) {}
}
}
if (options.noShowPopup !== true) {
const dom = this.context.ui.createElementByJsonData(content, options.popupShowInForm, {
width: options.popupWidth,
height: options.popupHeight
})
popup.setDOMContent(dom.element)
}
if (options.clickCallback) {
options.clickCallback(content);
}
}
if (options.noShowPopup !== true) {
popup.setLngLat(pt);
popup.addTo(this.map);
}
});
}
}
/**
* 手动指定公共对应点获取四参数
* @param {Object} [leftMapOptions] 左边地图的参数,可为空,默认当前地图
* @param {string} [leftMapOptions.mapid] 地图ID
* @param {string} [leftMapOptions.version] 地图版本(为空时采用当前打开的地图版本)
* @param {Object} [rightMapOptions] 右边地图的参数,可为空,高德地图
* @param {string} [rightMapOptions.mapid] 地图ID
* @param {string} [rightMapOptions.version] 地图版本(为空时采用当前打开的地图版本)
* @param {number} [leftMapOptions.webmaptype] 地图类型:1: 高德地图、2: 高德地图影像、3: 天地图、4: 天地图影像
* @returns {Promise<Object>} 返回参数,返回内容如下
* @example
* // 示例一
* // 手动指定公共对应点获取当前地图和互联网高德地图的四参数
* let fp = await this.getFourParamter();
* if (fp) {
* console.log("四参数", fp.fourParamter);
* }
* // 示例二
* // 手动指定公共对应点获取当前地图和cad地图sys_world的四参数
* let fp2 = await this.getFourParamter({}, {
* mapid: "sys_world"
* });
* if (fp2) {
* console.log("四参数", fp2.fourParamter);
* }
*/
async getFourParamter(leftMapOptions, rightMapOptions) {
return await this.context.ui.getFourParamter(leftMapOptions, rightMapOptions);
}
/**
* 切换至互联网地图做为底图
* @param {number} webmaptype 地图类型:1: 高德地图、2: 高德地图影像、3: 天地图、4: 天地图影像,可选,默认1
* @returns {Promise<Object>} 返回参数,返回内容如下
* @example
* // 示例一
* // 切换至高德地图做为底图
* let result = await this.switchWebMap(1);
* console.log("result", result);
* // 示例二
* // 切换至天地图影像做为底图
* let result2 = await this.switchWebMap(4);
* console.log("result2", result2);
*/
switchWebMap(webmaptype) {
if (!webmaptype) webmaptype = 1;
this.clearMapData({clearAllData: true});
let prj = new vjmap.LnglatProjection();
let webMapTiles = [];
if (webmaptype == 1 || webmaptype == 2) {
webMapTiles = vjcommon.gaodeProviderTiles(webmaptype == 2);
} else if (webmaptype == 3 || webmaptype == 4) {
webMapTiles = vjcommon.tiandituProviderTiles(webmaptype == 4);
}
let tiles = webMapTiles.map(t => vjcommon.getShardsTileUrl(t, this.map))
let sourceIds = [];
let layerIds = [];
for(let t of tiles) {
let id = vjmap.RandomID();
let sourceId = "web_source_" + webmaptype + "_" + id;
let layerId = "web_layer_" + webmaptype + "_" + id;
this.map.addRasterSource(sourceId, {
type: "raster",
tiles: t,
tileSize: 256
});
this.map.addRasterLayer(layerId, sourceId);
this.map.setMapProjection(prj);
sourceIds.push(sourceId);
layerIds.push(layerId);
}
return {
sourceIds: sourceIds,
layerIds: layerIds
}
}
/**
* 获取数据图层属性
* @param {string} layerName 图层名称,参数必填
* @returns {object | undefined} 返回图层属性对象, 如果没有找到图层,则返回undefined
* @example
* // 获取图层名称为layer1的数据图层属性
* let layerName = 'layer1';
* let property = this.getDataLayerProperty(layerName);
* console.log("图层属性:", property);
*/
getDataLayerProperty(layerName) {
let item = this.context.global.layers.find(layer => layer.layerName == layerName);
if(item) {
return item.layer.mapLayer
}
}
/**
* 获取地图的缩略图
* @param {object} options 参数 可选。
* @param {string} [options.mapid] 地图ID. 可选,为空时则获取当前打开的地图
* @param {string} [options.version] 版本号. 可选,为空时则获取当前打开的地图版本
* @param {number} [options.width] 缩略图宽度. 可选
* @param {number} [options.height] 缩略图高度. 可选
* @param {boolean} [options.darkTheme] 是否是深色主题. 可选
* @returns {string} 返回缩略图的URL地址
* @example
* // 示例一
* // 获取地图sys_hello的缩略图
* let url1 = this.thumbnailUrl({
* mapid: "sys_hello"
* });
* console.log(url1);
* // 示例二
* // 获取地图sys_hello的缩略图,版本v1, 宽度200,高度200,深色主题
* let url2 = this.thumbnailUrl({
* mapid: "sys_hello",
* version: "v1",
* width: 200,
* height: 200,
* darkTheme: true
* });
* console.log(url2);
*/
thumbnailUrl(options) {
let svc = this.map.getService();
return svc.thumbnailUrl(options.mapid, options.version, options.width, options.height, options.darkTheme);
}
/**
* 创建天空图层
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {string} options.skyType 天空类型,可选值: 'gradient'(渐变) 或 'atmosphere'(模拟大气),默认为'atmosphere'
* @param {string[]} options.skyGradient 渐变色数组,当skyType为'gradient'时有效
* @param {[number, number]} options.skyGradientCenter 渐变中心点坐标[经度, 纬度]
* @param {number} options.skyGradientRadius 渐变半径,梯度延伸的角距离(度),范围0-180,默认90
* @param {number} options.skyOpacity 天空透明度,范围0-1,默认1
* @param {string} options.skyAtmosphereColor 大气颜色,用于调整主要大气散射系数,默认白色
* @param {string} options.skyAtmosphereHaloColor 大气光晕颜色,应用于大气太阳光晕,默认白色
* @param {number} options.skyAtmosphereSunIntensity 大气太阳光强度,范围0-100,默认10
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 创建天空图层
* let layer = await this.createSkyLayer();
* console.log(layer);
*/
async createSkyLayer(options) {
options = options || {};
let type = "sky";
let randomId = vjmap.RandomID();
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
skyType: 'gradient',
skyGradient: [
'interpolate',
['linear'],
['sky-radial-progress'],
0.8,
'rgba(135, 206, 235, 1.0)',
1,
'rgba(0,0,0,0.1)'
],
skyGradientCenter: [0, 0],
skyGradientRadius: 90,
skyOpacity: [
'interpolate',
['exponential', 0.1],
['zoom'],
0,
0.3,
22,
1
],
...options,
type: type
}
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId: undefined, layerName, data: undefined, options, layer, visible: true, create: this.createSkyLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId: undefined,
layerId,
layerName
};
}
/**
* 选择绘制的图元
* @param {boolean} selectSingle 是否只能选择一个图元
* @returns {Promise<GeoJSON | undefined>} 返回选中的GeoJSON对象,如果取消操作则返回undefined
* @example
* const geojson = await this.actionSelect(true);
* if (!geojson) return;
* // 遍历geojson的每个feature
* for(let i = 0; i < geojson.features.length; i++) {
* let feature = geojson.features[i];
* console.log(feature);
* }
*/
async actionSelect(selectSingle) {
this.context.tools.logInfo("请选择绘制的图元")
let draw = this.map.getDrawLayer();
let selection = await vjmap.Draw.actionSelect(this.map, draw, {
selectSingle
});
if (selection.cancel) {
return;
}
return this.map.fromLngLat(selection);
}
/**
* 删除一个信息窗口Popup
* @param {string} id 信息窗口Popup的id
* @example
* this.deletePopup("popup_1"); // 删除id为Popup_1的信息窗口
*/
deletePopup(id) {
let popups = this.context.global.popups || [];
let item = popups.find(m => m.id == id);
if (item) {
item.popup.remove()
let pt = this.map.fromLngLat(item.popup.getLngLat());
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.addPopup(pt, item.options);
});
}
}
/**
* 根据json数据弹出数据信息框
* @param {object|Array} json json数据内容,json对象或数组, 必填
* @param {boolean} showForm 是否以表单格式显示,参数可选,默认false
* @param {string} title 对话框标题,参数可选
* @example
* // 示例一
* // 获取地图的当前中心点坐标X和Y、地图级别,生成json对象,根据这数据弹出数据信息框,标题为"地图信息",以表单格式显示
* let center = this.getCenter();
* const mapInfo = {
* centerX: center[0],
* centerY: center[1],
* zoom: map.getZoom()
* }
* await this.showDataInfoDialog(mapInfo, true, "地图信息");
* // 示例二
* // 矩形选择图中实体对象,根据选择的实体数据弹出数据信息框,标题为"实体信息",信息框关闭后清除所有选择的实体对象
* const geojson1 = await this.selectFeatures({
* isPointSel: true
* });
* await this.showFormInputDialog(geojson1, false, "实体信息");
* // 清除所有选择的实体对象
* this.clearSelectedFeatures();
*/
async showDataInfoDialog(json, showForm, title) {
return await this.context.ui.showInfoDlg(json, showForm, title)
}
/**
* 创建扩散的点数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {string} options.text 要显示的文字,参数可选,默认为"index ${props.index}"
* @param {number} options.minZoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxZoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isDrawLinePoint 绘制点时包括线上所有点, 参数可选,默认为false
* @param {number} options.width 像素宽度,参数可选,默认为24
* @param {number} options.markerHeight 像素高度,参数可选
* @param {string} options.color1 颜色值一,参数可选
* @param {string} options.color2 颜色值二,参数可选
* @param {number} options.textFontSize 字体大小,参数可选
* @param {string} options.textColor 字体颜色,参数可选
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {string} options.animationType 动画类型,参数可选,无动画 MAP_ANIMATION_NONE,弹跳 MAP_ANIMATION_BOUNCE, 坠落 MAP_ANIMATION_DROP
* @param {number} options.rotation 旋转角度,参数可选
* @param {string} options.rotationAlignment 旋转对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {string} options.pitchAlignment 倾斜对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {number} options.height 坐标标高值,参数可选
* @param {number} options.scaleMaxZoom 能缩放的最大级别,参数可选
* @param {boolean} options.removeWhenNoInMapView 视图范围内自动显隐,参数可选
* @param {number} options.removeWhenNoInMapViewPadding 视图范围内自动显隐Padding像素范围,参数可选,默认500
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer9的扩散的点数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createDiffusedMarkerLayer(geojson, {
* layerName: 'layer9'
* });
* console.log(layer);
*/
async createDiffusedMarkerLayer(data, options) {
options = options || {};
let type = "diffusedMarker";
if (options.height) {
options.height = this.map.geoLengthToHeight(options.height);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
text: "ID:${props.index}",
width: 30,
markerHeight: 30,
color1: "#ff0000",
color2: "#ffff00",
textFontSize: 15,
textColor: "#00ffff",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createDiffusedMarkerLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 根据给定的点坐标数组直接绘制一条线
* @param {[number, number][]} points 线的坐标点数组[[x1,y1],[x2,y2],...],参数必填
* @param {object} options 配置参数, 参数可选
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {number} options.width 线宽,默认1,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Object} 返回绘制的图元对象,返回的对象包含以下属性:
* @example
* // 根据三个地图上面的随机点创建一条线, 线的宽度为2-5,颜色红色
* let points = this.randomPointInMapBounds(3); // 每条线3个点
* const line = this.createLineString(points, {
* color: vjmap.randomColor(),
* width: 2 + Math.random() * 3
* });
* console.log(`线 ID:`, line.id);
* console.log(`线 点坐标:`, line.points);
*/
createLineString(points, options) {
let draw = this.map.getDrawLayer();
let id = vjmap.RandomID(10);
draw.add({
id,
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: points.map(pt => this.map.toLngLat(pt))
},
properties: {
color: options.color,
line_width: options.width,
data: options.data
}
});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.getDrawLayer().delete(id);
});
return {
id,
points
};
}
/**
* 移除地图比例尺控件
* @example
* // 移除地图比例尺控件
* this.removeMapScaleControl();
*/
removeMapScaleControl() {
this.context.global.controls = this.context.global.controls || {};
let options;
if (this.context.global.controls.scaleControl) {
options = {...this.context.global.controls.scaleControl.options}
this.map.removeControl(this.context.global.controls.scaleControl.control);
delete this.context.global.controls.scaleControl;
}
// 增加撤销记录
this.context.ui.addUndoRecord(() => {
if (options) {
this.addMapScaleControl(options);
}
});
}
/**
* 增加地图导航条控件
* @param {boolean} options.showCompass 是否显示指南针,参数可选
* @param {boolean} options.showZoom 是否显示缩放按钮,参数可选
* @param {boolean} options.visualizePitch 是否显示倾斜按钮,参数可选
* @param {'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'} options.position 控件位置,参数可选, 默认'top-right'
* @example
* // 增加地图导航条控件,位置左上角
* this.addMapNavigationControl('top-left');
*/
addMapNavigationControl(options) {
options = options || {};
this.context.global.controls = this.context.global.controls || {};
if (this.context.global.controls.navigationControl) {
this.map.removeControl(this.context.global.controls.navigationControl.control);
}
let navigationControl = new vjmap.NavigationControl(options);
this.map.addControl(navigationControl, options.position);
this.context.global.controls.navigationControl = {
control: navigationControl,
position: options.position,
options: options
};
// 增加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.removeControl(navigationControl);
delete this.context.global.controls.navigationControl;
});
}
/**
* 取消监听地图事件
* @param {string} eventName 事件名称,参数必填, 事件名称有:
* @param {function | undefined} callback 事件回调函数,可为空,为空时取消所有事件,默认undefined
* @example
* // 取消监听地图单击事件
* this.cancelListenMapEvent("click");
*/
cancelListenMapEvent(eventName, callback) {
if (eventName === "clickEntity") {
this.map.disableLayerClickHighlight();
if (this.context.global._delegatedListeners) delete this.context.global._delegatedListeners[eventName];
return;
}
if (callback) {
this.map.off(eventName, callback);
if (this.context.global._delegatedListeners && this.context.global._delegatedListeners[eventName]) {
let index = this.context.global._delegatedListeners[eventName].indexOf(callback);
if (index !== -1) {
this.context.global._delegatedListeners[eventName].splice(index, 1);
}
}
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.on(eventName, callback);
this.context.global._delegatedListeners = this.context.global._delegatedListeners || {};
this.context.global._delegatedListeners[eventName] = this.context.global._delegatedListeners[eventName] || [];
this.context.global._delegatedListeners[eventName].push(callback);
});
} else {
if (!this.context.global._delegatedListeners) return;
if (!this.context.global._delegatedListeners[eventName]) return;
const listener = this.context.global._delegatedListeners[eventName];
if (listener) {
let saveListener = [...listener];
listener.forEach(func => {
this.map.off(eventName, func);
if (this.context.global._delegatedListeners && this.context.global._delegatedListeners[eventName]) {
let index = this.context.global._delegatedListeners[eventName].indexOf(func);
if (index !== -1) {
this.context.global._delegatedListeners[eventName].splice(index, 1);
}
}
});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
saveListener.forEach(func => {
this.map.on(eventName, func);
this.context.global._delegatedListeners = this.context.global._delegatedListeners || {};
this.context.global._delegatedListeners[eventName] = this.context.global._delegatedListeners[eventName] || [];
this.context.global._delegatedListeners[eventName].push(func);
});
});
}
}
}
/**
* 创建热力图数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {Array|number} options.heatmapWeight 热力图权重,表示点对热力图的贡献度,值越大显示越明显,参数可选,默认值为[0,1]区间
* @param {Array|number} options.heatmapRadius 热力图点的计算半径,单位为像素,参数可选,默认值为30
* @param {Array|string} options.heatmapColor 热力图的颜色配置,可设置不同数值的颜色表现,参数可选
* @param {Array|number} options.heatmapIntensity 热力图整体强度,类似权重但作用于整体,参数可选,默认值随缩放级别在1-3之间
* @param {Array|number} options.heatmapOpacity 热力图透明度,取值范围0-1,参数可选,默认值随缩放级别变化
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer16的热力图数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createHeatmapLayer(geojson, {
* layerName: 'layer16'
* });
* console.log(layer);
*/
async createHeatmapLayer(data, options) {
options = options || {};
let type = "heatmap";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
// heatmapWeight:表示一个点对热力图权重的贡献,在贡献越大的地方热力图显示应该越明显
heatmapWeight: [
'interpolate',
['linear'],
['get', 'value'],
0, // 因为上面用了0,100,最小和最大值,把这两个最小和最大值归化到0,1区间
0,
100,
1
],
// heatmapRadius:热力图的一个点计算权重的时候计算的点的半径,单位为像素,默认为30
heatmapRadius: [
'interpolate',
['linear'],
['zoom'],
0, //0级别
10, //半径10
9, // 9级别 (其余级别,线性插值)
50 // 半径50
],
// heatmapColor:热力图的颜色,设置在各个热力图的数值上是什么颜色
heatmapColor: [
'interpolate',
['linear'],
['heatmap-density'],
0,
'rgba(33,102,172,0)',
0.2,
'rgb(103,169,207)',
0.4,
'rgb(209,229,240)',
0.6,
'rgb(253,219,199)',
0.8,
'rgb(239,138,98)',
1,
'rgb(178,24,43)'
],
//heatmapIntensity:热力图强度,有点类似于heatmapWeight属性,但是该属性是设置整体上热力图的强度
heatmapIntensity: [
'interpolate',
['linear'],
['zoom'],
0,
1,
9,
3
],
//heatmapOpacity:热力图的透明度
heatmapOpacity: [
'interpolate',
['linear'],
['zoom'],
7,
1,
9,
0
],
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加��层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createHeatmapLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 给定路线、起始点、终点生成最短路径
* @param {Object} options 配置参数
* @param {[number, number]} options.startPoint 起点 [lng, lat]
* @param {[number, number]} options.endPoint 终点 [lng, lat]
* @param {number} [options.precision] 误差,小数点后几位,默认为0
* @param {boolean} [options.hasDirection] 是否考虑方向,默认为false
* @param {boolean} [options.disableDrawPath] 禁止自动绘制路径,默认为false
* @param {string} [options.drawPathColor] 绘制路径颜色,默认为"#ff0000"
* @param {number} [options.drawPathWidth] 绘制路径宽度,默认为5
* @returns {Object | null} 返回最短路径信息,包括起点、终点、路径和详细路径信息、绘制路径的图层id、绘制路径的图层源id, 如果返回null,则表示没有找到路径, 具体返回参数如下:
* @example
* // 给定路线[[116.397428, 39.90923], [116.405285, 39.90923]]、[[116.405285, 39.90923], [116.425285, 39.91923]]、起始点[116.397428, 39.90923]、终点[116.405285, 39.90923]生成最短路径
* let result = this.findShortestPath({
* startPoint: [116.397428, 39.90923],
* endPoint: [116.405285, 39.90923],
* lines: [
* {points: [[116.397428, 39.90923], [116.405285, 39.90923]]},
* {points: [[116.405285, 39.90923], [116.425285, 39.91923]]},
* ]
* });
* console.log(result);
*/
findShortestPath(options) {
options = options || {};
options.lines = options.lines || [];
options.lines = options.lines.map(line => {
return {
points: line.points.map(point => vjmap.geoPoint(point)),
id: line.id,
weight: line.weight
}
});
let result = vjmap.findShortestPath(vjmap.geoPoint(options.startPoint), vjmap.geoPoint(options.endPoint), options.lines, options.precision, options.hasDirection);
if (result.error) {
this.map.logInfo(result.error.message, "error");
return null;
}
if (options.disableDrawPath !== true) {
let polyline = new vjmap.Polyline({
data: this.map.toLngLat(result.route),
lineColor: options.drawPathColor || "#ff0000",
lineWidth: options.drawPathWidth || 5,
});
polyline.addTo(this.map);
result.lineLayerId = polyline.layerId;
result.lineSourceId = polyline.sourceId;
}
let sourceId = result.lineSourceId;
if (sourceId) {
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.removeSourceEx(sourceId);
});
}
return result;
}
/**
* 获取地图容器的大小
* @returns {object} 包含width和height的对象,表示地图容器的宽度和高度
*/
getContainerSize() { this.map.getContainer().getBoundingClientRect() }
/**
* 通过json数据创建新的地图
* @param {array} drawJson `drawJson` 是一个 JSON 数组,其中每个元素为一个图形实体的定义对象。绘制的坐标系是标准的迪卡尔坐标系。所有实体均继承自 `DbEntity`,并根据具体的图形类型提供额外的属性。以下是支持的实体类型及其格式:
* @param {object} options 其他参数(可选), 支持以下参数:
* @example
* // 示例一
* // 创建中心点为[10,10],半径为5,颜色为红色的圆
* let drawJson = [{
* "typename": "DbCircle",
* "center": [10, 10, 0],
* "radius": 5,
* "color": "#FF0000"
* }]
* // 创建一个多段线,包含两个点[10,10]和[20,20],颜色为绿色,线宽为2
* drawJson.push({
* "typename": "DbPolyline",
* "points": [[10, 10], [20, 20]],
* "color": "#00FF00",
* "lineWidth": 2
* })
* // 通过上面生成的drawJson数据创建地图
* let mapid = await this.createMapData(drawJson);
* console.log(mapid)
* // 示例二
* // 基于图sys_hello/v1,选择图中的"TEXT"图层,创建一个单行文本,位置为[10108,10556],文本高2000,文本内容为"Hello, World!",水平居中对齐,垂直居中对齐, 再创建一个填充的三角形,坐标为[[10,10],[20,20],[10,20]],填充颜色为#00FF00, 图层名称为"ly1"
* await this.createMapData([{
* "typename": "DbText",
* "position": [10108, 10556, 0],
* "height": 2000,
* "text": "Hello, World!",
* "horizontalMode": 1,
* "verticalMode": 2
* }, {
* "typename": "DbHatch",
* "points": [[10, 10], [20, 20], [10, 20]],
* "color": "#00FF00",
* "layer": "ly1"
* }], {
* "from": "sys_hello/v1",
* "pickLayers": ["TEXT"]
* });
* // 示例三
* // 在位置[10, 10], [5000, 0], [0, 6000], 在这三个位置分别创建块参照,这三个块参照都来源于克隆图形sys_symbols,克隆实体id为25B。第二个块参照旋转45,第三个块参照放大3倍, 创建完直接加到当前地图中
* await this.createMapData([{
* "typename": "DbBlockReference",
* "position": [10, 10],
* "cloneFromDb": "sys_symbols",
* "cloneObjectId": "25B"
* }, {
* "position": [5000, 0],
* "cloneFromDb": "sys_symbols",
* "cloneObjectId": "25B",
* "rotateMat": 45
* }, {
* "position": [0, 6000],
* "cloneFromDb": "sys_symbols",
* "cloneObjectId": "25B",
* "scaleMat": 3
* }], {
* "openway": 1
* });
*/
async createMapData(drawJson, options) {
return await this.context.tools.createMapData(this.context, drawJson, options)
}
/**
* 取消测量
* @example
* // 取消测量
* await this.cancelMeasure();
*/
cancelMeasure() {
vjcommon.runMeasureCmd(this.map, "measureCancel");
}
/**
* 监听地图事件
* @param {string} eventName 事件名称,参数必填, 事件名称有:
* @param {function} callback 事件回调函数,参数必填
* @param {boolean} isOnce 是否只触发一次,默认false,参数可选
* @example
* // 示例一
* // 监听地图双击事件
* this.listenMapEvent("dblclick", ev => {
* console.log(ev);
* });
* // 示例二
* // 监听地图单击事件,只触发一次,在点击的位置增加一个Marker
* this.listenMapEvent("click", ev => {
* if (ev && ev.point) {
* // 获取当前地图的像素坐标,转换为CAD图几何坐标
* const point = this.pixelPointToGeoPoint(ev.point);
* // 根据获取的CAD图几何坐标位置,在地图上增加一个Marker
* this.addMarker(point);
* }
* }, true);
* // 示例三
* // 监听地图实体点击事件,通过点击内容显示数据面板,面板标题为"实体数据",面板高度为500,面板以表单形式显示
* this.listenMapEvent("clickEntity", ev => {
* if (ev) {
* this.showDataPanel(ev, { title: "实体数据", height: 500, showForm: true });
* } else {
* this.closeDataPanel();
* }
* });
*/
listenMapEvent(eventName, callback, isOnce) {
// callback的参数中含有当前地图的像素坐标,可通过 this.pixelPointToGeoPoint(ev.point) 转换为CAD图几何坐标
// 将事件监听器添加到全局对象中,以便在取消监听时使用
this.context.global._delegatedListeners = this.context.global._delegatedListeners || {};
this.context.global._delegatedListeners[eventName] = this.context.global._delegatedListeners[eventName] || [];
this.context.global._delegatedListeners[eventName].push(callback);
if (eventName === "clickEntity") {
if (this.context.global._delegatedListeners[eventName].length === 1) {
// 第一次监听
let oldEventValue;
this.map.enableLayerClickHighlight(this.map.getService(), e => {
if (e !== oldEventValue) {
this.context.global._delegatedListeners[eventName].forEach(func => {
func(e);
});
oldEventValue = e;
}
})
}
return;
}
if (isOnce) {
this.map.once(eventName, callback);
} else {
this.map.on(eventName, callback);
}
if (!this.context.global.__listenKeyEvent) {
// 先主动监听键盘事件,以便获取当前是否按下Shift、Ctrl、Alt键
this.context.global.__listenKeyEvent = vjcommon.listenKeyEvent()
}
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.off(eventName, callback);
if (this.context.global._delegatedListeners && this.context.global._delegatedListeners[eventName]) {
let index = this.context.global._delegatedListeners[eventName].indexOf(callback);
if (index !== -1) {
this.context.global._delegatedListeners[eventName].splice(index, 1);
}
}
});
}
/**
* 打开图, 通过指定地图id或手动选择打开图
* @param {object} options 参数 可选
* @param {string} options.mapid 地图id 可选,为空时则手动选择地图
* @param {string} options.version 版本 可选,为空时则选择最新版本
* @param {"Memory" | "GeomRaster" | "GeomVector"} options.openway 打开方式 可选,Memory: 内存, GeomRaster: 栅格, GeomVector: 矢量, 默认栅格
* @param {boolean} options.lightTheme 是否使用浅色主题 可选,为空时则使用深色主题
* @param {boolean} options.isFullExtent 是否全图范围显示 可选,默认false
* @returns {Object | undefined} 返回对象,包含地图id和版本 {mapid: string, version: string}, 如果取消则返回undefined
* @example
* // 示例一
* // 打开地图
* this.openCadMap();
* // 示例二
* // 打开地图sys_world
* this.openCadMap({
* mapid: "sys_world"
* });
* // 示例三
* // 打开地图sys_hello, 版本v1, 以矢量方式打开, 使用浅色主题
* this.openCadMap({
* mapid: "sys_hello",
* version: "v1",
* openway: "GeomVector",
* lightTheme: true
* });
*/
async openCadMap(options) {
options = options || {};
let mapid = options.mapid || "";
let version = options.version || "";
let mapopenway = vjmap.MapOpenWay.GeomRender;
let isVectorStyle = false;
let lightTheme = options.lightTheme ?? (this.map.getService().currentMapParam()?.darkMode === true ? false : true)
if (!mapid) {
let res = await this.context.ui.selectCadMap()
if (!res) return;
mapid = res.mapid;
version = res.version;
mapopenway = res.openway;
isVectorStyle = res.isVectorStyle;
} else {
mapopenway = options.openway === "Memory" ? vjmap.MapOpenWay.Memory : vjmap.MapOpenWay.GeomRender;
isVectorStyle = options.openway === "GeomVector" ? true : false;
}
// 打开地图
let res = await this.map.switchMap({
mapid: mapid,
version: version,
mapopenway: mapopenway,
style: lightTheme ? vjmap.openMapLightStyle() : vjmap.openMapDarkStyle(),
// 如果有密码保护的图,要求输入密码回调,如果不传回调函数的话,将用系统的prompt提示输入
cbInputPassword: (param) => {
return new Promise((resolve) => {
if (param.tryPasswordCount > 3) {
resolve('');// 如果密码重试次数超过三次
} else {
this.messageBoxPrompt("请输入密码或AccessKey", `打开图 ${param.mapid} 需要密码`, "", true)
.then((value) => {
resolve(value) ;
})
.catch(() => {
resolve('') ;
})
}
})
}
}, false, isVectorStyle, true, true);
if (options.isFullExtent !== true) {
// 用默认的中心点和缩放级别参数
let svc = this.map.getService();
let param = svc.currentMapParam();
if (param && param.view) {
if (param.view.center) {
this.map.setCenter(param.view.center);
}
if (param.view.zoom) {
this.map.setZoom(param.view.zoom);
}
if (param.view.bearing) {
this.map.setBearing(param.view.bearing);
}
}
}
if (res && res.error) {
this.showMessage(res.error)
return;
}
// 打开新的地图了,需要清空历史记录
this.context.ui.clearUndoRecord();
return {
mapid: mapid,
version: version
};
}
/**
* 通过范围创建空白地图
* @param {number| [number,number,number,number] | GeoJSON} boundData 范围数据,可以是一个数值,表示地图宽高, 或一个数组表示地图范围, 或一个GeoJSON对象(会自动计算范围), 要为空时,则创建一个10000*10000的正方形地图
* @example
* // 示例一
* // 创建一个空白地图
* this.createBlankMap();
* // 示例二
* // 通过范围[100,100,200,200]创建空白地图
* this.createBlankMap([100,100,200,200]);
*/
createBlankMap(boundData) {
let dataExtent;
boundData = boundData || 10000;
if (typeof boundData === "number") {
dataExtent = [-boundData/2, -boundData/2, boundData/2, boundData/2 ];
} else if (Array.isArray(boundData) && boundData.length === 4 && typeof boundData[0] === "number") {
dataExtent = vjmap.GeoBounds.fromArray(boundData);
} else {
dataExtent = vjmap.GeoBounds.fromDataExtent(boundData);
}
dataExtent = dataExtent.square(); // 地图的范围需要正方形,地图范围需要正方形才可以
let mapBounds = dataExtent.scale(4); // 把范围调点大,这个值可以根据情况自己定,以后加的点不要超过此范围即可
// 清空地图
this.clearMapData({
clearAllData: true
});
// 根据地图范围建立几何投影坐标系
let prj = new vjmap.GeoProjection(mapBounds);
let blankStyle = this.map.getService().rasterBlankStyle(0, 24)
let sourceId = Object.keys(blankStyle.sources)[0];
this.map.addRasterSource(sourceId, blankStyle[sourceId]);
this.map.addLayer(blankStyle.layers[0]);
this.map.setMapProjection(prj);
// 打开新的地图了,需要清空历史记录
this.context.ui.clearUndoRecord();
}
/**
* 将地图像素点坐标转换为CAD图几何坐标
* @returns {[number, number]} 返回包含两个数字的数组[x,y],表示CAD图几何坐标
* @example
* // 将地图像素点坐标[5, 10]转换为CAD图几何坐标
* let point = this.pixelPointToGeoPoint([5, 10]);
* let x = point[0];
* let y = point[1];
* console.log(x, y);
*/
pixelPointToGeoPoint(pt) {
let lnglat = this.map.unproject(pt);
let geoPoint = this.map.fromLngLat(lnglat);
return [geoPoint.x, geoPoint.y];
}
/**
* 移除地图导航条控件
* @example
* // 移除地图导航条控件
* this.removeMapNavigationControl();
*/
removeMapNavigationControl() {
this.context.global.controls = this.context.global.controls || {};
let options;
if (this.context.global.controls.navigationControl) {
options = {...this.context.global.controls.navigationControl.options}
this.map.removeControl(this.context.global.controls.navigationControl.control);
delete this.context.global.controls.navigationControl;
}
// 增加撤销记录
this.context.ui.addUndoRecord(() => {
if (options) {
this.addMapNavigationControl(options);
}
});
}
/**
* 飞行至目标位置
* @param {Object} options 缩放选项
* @param {[number,number]} options.center 目标位置的坐标
* @param {number} [options.zoom] 目标缩放级别。可选,如果不提供,则保持当前缩放级别
* @param {number} [options.bearing] 目标方位角(旋转角度),以度为单位,0 表示正北。可选,如果不提供,则保持当前方位角
* @param {number} [options.pitch] 目标倾斜角度,以度为单位,0 表示垂直于地图。可选,如果不提供,则保持当前倾斜角度
* @param {number} [options.speed] 飞行速度的倍数。默认值为 1.2。可选
* @param {number} [options.curve] 飞行路径的曲率,值越大路径越弯曲。默认值为 1.42。可选
* @param {boolean} [options.essential] 是否为必要动画,如果为true,则不会受到用户设置的“减少运动”选项的影响。可选,默认值为true
* @param {boolean} [options.useAsync] 是否使用异步动画,如果为true,则使用异步动画。可选,默认值为false
* @param {boolean} [options.easeTo] 是否使用缓动动画,如果为true,则使用缓动动画。可选,默认值为false
* @example
* // 示例一
* // 飞行至目标位置[100,20]
* this.flyTo({center: [100,20]});
* // 示例二
* // 飞行至目标位置[100,20],缩放级别为10,旋转角度为45度,倾斜角度为60度,使用缓动动画
* this.flyTo({center: [100,20], zoom: 10, bearing: 45, pitch: 60, easeTo: true});
*/
async flyTo(options) {
options = options || {};
if (options.center) {
options.center = this.map.toLngLat(options.center);
}
options.essential = options.essential === undefined ? true : options.essential;
let oldCenter = this.map.getCenter();
let oldZoom = this.map.getZoom();
let oldBearing = this.map.getBearing();
let oldPitch = this.map.getPitch();
let method = options.easeTo === true ? "easeTo" : "flyTo";
if (options.useAsync === true) {
function animateAsync(options) {
return new Promise((resolve) => {
// 监听地图移动结束事件
this.map.once('moveend', ({ originalEvent }) => {
if (!originalEvent) {
resolve()
}
});
this.map[method](options);
});
}
await animateAsync(options);
} else {
this.map[method](options)
}
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.setCenter(oldCenter);
this.map.setZoom(oldZoom);
this.map.setBearing(oldBearing);
this.map.setPitch(oldPitch);
});
}
/**
* 创建背景图层
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {string} options.backgroundColor 背景颜色,参数可选,支持十六进制颜色值,默认为"#572782"
* @param {number} options.backgroundOpacity 背景透明度,参数可选,取值范围0-1,默认为0.6
* @param {string} options.backgroundPattern 背景图案,参数可选,填充图片名称,图片需要预先加载到地图中图像宽度必须是2的系数(2、4、8、...、512)
* @param {boolean} options.visible 图层是否可见,参数可选,默认为true
* @param {string} options.before 图层在指定图层之前绘制,参数可选
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 创建背景图层,背景颜色为#572782,背景透明度为0.6
* let layer = await this.createBackgroundLayer({
* backgroundColor: "#572782",
* backgroundOpacity: 0.6,
* });
* console.log(layer);
*/
async createBackgroundLayer(options) {
options = options || {};
let type = "background";
let randomId = vjmap.RandomID();
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
backgroundColor: "#572782",
backgroundOpacity: 0.6,
...options,
type: type
}
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId: undefined, layerName, data: undefined, options, layer, visible: true, create: this.createBackgroundLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId: undefined,
layerId,
layerName
};
}
/**
* 设置绘制图元属性
* @param {string} id 图元ID,参数必填
* @param {object} options 配置参数, 参数可选
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {string} options.borderColor 边框颜色,格式为#RRGGBB,参数可选
* @param {number} options.opacity 透明度,参数可选
* @param {number} options.borderOpacity 边框透明度,参数可选
* @param {number} options.width 线宽,参数可选
* @param {number} options.extrusionHeight 拉伸高度,多边形有效,参数可选
* @param {boolean} options.isOff 是否隐藏, false显示,true隐藏,参数可选
* @param {boolean} options.isLocked 是否锁定, false不锁定,true锁定,参数可选
* @param {string} options.text 文本内容, 符号文本有效,参数可选
* @param {[number, number][] | [number, number]} options.coordinates 坐标点数组,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 示例一
* // 选择绘制的图元,把颜色改为红色
* let geojson = await this.actionSelect();
* for(let feature of geojson.features) {
* this.setFeatureProperty(feature.id, {
* color: "#FF0000",
* });
* };
* // 示例二
* // 获取绘制的全部图元的第二个实体,设置颜色为黄色,透明度0.6,线宽2,边框颜色为红色
* let geojson2 = this.getDrawAll();
* let features = geojson2.features;
* let index = 2; // 第二个实体
* if (index >= features.length) {
* return;
* }
* let feature = features[index];
* this.setFeatureProperty(feature.id, {
* color: "#FFFF00",
* opacity: 0.6,
* width: 2,
* borderColor: "#FF0000",
* });
*/
setFeatureProperty(id, options) {
options = options || {};
let draw = this.map.getDrawLayer();
let attr = {
"color": options.color,// 颜色
"opacity": options.opacity, // 透明度
"outlineColor": options.borderColor,// 边框颜色
"line_width": options.width, // 线宽
"line_opacity": options.borderOpacity, // 透明度
"data": options.data,
"coordinates": options.coordinates,
"isOff": options.isOff,
"isLocked": options.isLocked,
"text": options.text,
"extrusionHeight": options.extrusionHeight,
};
for(let key in attr) {
if (attr[key] !== undefined && attr[key] !== null) {
draw.setFeatureProperty(
id,
key,
attr[key]
);
}
}
draw.forceRefresh();
}
/**
* 保存临时数据
* @param {string} name 数据名
* @param {any} data 数据内容
* @example
* // 将"123"保存到临时数据test中
* this.saveTempData("test", "123");
*/
saveTempData(name, data) {
this.context.resultData[name] = data
}
/**
* 删除数据图层
* @param {string} layerName 图层名称,参数必填
* @example
* // 删除图层名称为layer1的数据图层
* let layerName = 'layer1';
* this.deleteDataLayer(layerName);
*/
deleteDataLayer(layerName) {
let item = this.context.global.layers.find(layer => layer.layerName == layerName);
if(item) {
let layerId = item.layerId;
let sourceId = item.sourceId;
if (item.layer && typeof(item.layer.getLayerId) == "function") {
layerId = item.layer.getLayerId();
let style = this.map.getStyle();
let layer = style.layers.find(layer => layer.id == layerId);
if (layer && layer.source) {
sourceId = layer.source;
}
}
item.layer.removeLayer(this.map, layerId);
if (sourceId) {
this.map.removeSourceEx(sourceId);
}
this.context.global.layers = this.context.global.layers.filter(layer => layer.layerId != item.layerId);
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
if (item.create) {
item.create(item.data, item.options);
}
});
}
}
/**
* 设置数据图层悬浮显示弹窗内容
* @param {string} layerName 图层名称,参数必填
* @param {Record<string, any>} options 其他参数,参数可选
* @param {any} options.popupContent 弹窗内容,默认为当前数据的属性值
* @param {boolean} options.popupShowInForm 弹窗内容是否以表单形式显示,参数可选,默认为false
* @param {[number, number]} options.popupOffset 弹窗偏移量,参数可选,默认为[0, -40]
* @param {number} options.popupWidth 弹窗宽度,参数可选
* @param {number} options.popupHeight 弹窗高度,参数可选
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选,默认为true
* @param {boolean} options.noShowPopup 是否不显示弹窗,参数可选,默认为false
* @param {Function} options.hoverCallback 悬浮回调函数,参数可选,默认为null,(回调函数第一个参数为弹窗内容)
* @example
* // 示例一
* // 设置图层名称为layer1的数据图层悬浮显示弹窗
* this.setDataLayerHoverPopupContent('layer1');
* // 示例二
* // 设置图层名称为layer1的数据图层悬浮显示弹窗, 弹窗内容为"ID:${props.index}"
* this.setDataLayerHoverPopupContent('layer1', {
* popupContent: "ID:${props.index}",
* });
*/
setDataLayerHoverPopupContent(layerName, options) {
let item = this.context.global.layers.find(layer => layer.layerName == layerName);
if(!item) return;
options = options || {};
options.isHoverPointer = options.isHoverPointer === undefined ? true : options.isHoverPointer;
let markers = item.layer.markers;
if (Array.isArray(markers)) {
// 如果是Marker相关的图层
for(let m = 0; m < markers.length; m++) {
let marker = markers[m];
let oldCursor;
marker.on('mouseover', () => {
if (options.isHoverPointer) {
oldCursor = this.map.getCanvas().style.cursor;
this.map.getCanvas().style.cursor = "pointer";
}
let popup = this.context.global.popup;
if (!popup && options.noShowPopup !== true) {
popup = new vjmap.Popup({
offset: options.popupOffset || [0, -40],
closeOnClick: false,
closeButton: true,
anchor: "bottom"
})
this.context.global.popup = popup;
}
if (options.noShowPopup !== true &&options.popupOffset) {
popup.setOffset(options.popupOffset);
}
let content = options.popupContent;
if (item.data && item.data.features && item.data.features[m] && item.data.features[m].properties && typeof content == "string" && !(content in item.data.features[m].properties)) {
content = item.layer.evalValue("return `" + content + "`", item.data.features[m].properties, this.map);
if (options.noShowPopup !== true) {
popup.setHTML(content);
}
if (options.hoverCallback) {
options.hoverCallback(content);
}
} else {
if (!content) {
content = item.data.features[m].properties;
} else {
// 如果content是对象的属性,则获取对象的属性值
if (item.data.features[m].properties && (content in item.data.features[m].properties)) {
content = item.data.features[m].properties[content];
try {
// 如果content是json字符串,则尝试转换为对象
content = JSON.parse(content);
} catch (e) {}
}
}
if (options.noShowPopup !== true) {
const dom = this.context.ui.createElementByJsonData(content, options.popupShowInForm, {
width: options.popupWidth,
height: options.popupHeight
})
popup.setDOMContent(dom.element)
}
if (options.hoverCallback) {
options.hoverCallback(content);
}
}
if (options.noShowPopup !== true) {
popup.setLngLat(marker.getLngLat());
popup.addTo(this.map);
}
});
marker.on('mouseleave', () => {
if (options.isHoverPointer && oldCursor) {
this.map.getCanvas().style.cursor = oldCursor;
oldCursor = null;
}
if (options.noShowPopup !== true) {
let popup = this.context.global.popup;
if (popup) {
popup.remove();
this.context.global.popup = null;
}
}
if (options.hoverCallback) {
options.hoverCallback(null);
}
});
}
} else {
// 如果是其他图层
let oldCursor;
let oldFeatureId;
let layerId = item.layerId;
let sourceId = item.sourceId;
if (item.layer && typeof(item.layer.getLayerId) == "function") {
layerId = item.layer.getLayerId();
let style = this.map.getStyle();
let layer = style.layers.find(layer => layer.id == layerId);
if (layer && layer.source) {
sourceId = layer.source;
}
}
this.map.on("mouseover", layerId, (ev) => {
if (!ev || !ev.features || !ev.features[0]) return;
if (options.isHoverPointer) {
oldCursor = this.map.getCanvas().style.cursor;
this.map.getCanvas().style.cursor = "pointer";
}
let pt = ev.lngLat;
let popup = this.context.global.popup;
if (!popup && options.noShowPopup !== true) {
popup = new vjmap.Popup({
offset: options.popupOffset || [0, -40],
closeOnClick: false,
closeButton: true,
anchor: "bottom"
})
this.context.global.popup = popup;
}
if (options.noShowPopup !== true && options.popupOffset) {
popup.setOffset(options.popupOffset);
}
let content = options.popupContent;
if (ev.features[0].properties && typeof content == "string" && !(content in ev.features[0].properties)) {
content = item.layer.evalValue("return `" + content + "`", ev.features[0].properties, this.map);
if (options.noShowPopup !== true) {
popup.setHTML(content);
}
if (options.hoverCallback) {
options.hoverCallback(content);
}
} else {
if (!content ) {
content = ev.features[0].properties;
} else {
// 如果content是对象的属性,则获取对象的属性值
if (ev.features[0].properties && (content in ev.features[0].properties)) {
content = ev.features[0].properties[content];
try {
// 如果content是json字符串,则尝试转换为对象
content = JSON.parse(content);
} catch (e) {}
}
}
if (options.noShowPopup !== true) {
const dom = this.context.ui.createElementByJsonData(content, options.popupShowInForm, {
width: options.popupWidth,
height: options.popupHeight
})
popup.setDOMContent(dom.element)
}
if (options.hoverCallback) {
options.hoverCallback(content);
}
}
if (options.noShowPopup !== true) {
popup.setLngLat(pt);
popup.addTo(this.map);
}
oldFeatureId = ev.features[0].id;
this.map.setFeatureState({ source: sourceId, sourceLayer: layerId, id: ev.features[0].id }, { hover: true });
});
this.map.on("mouseleave", layerId, () => {
if (options.isHoverPointer && oldCursor) {
this.map.getCanvas().style.cursor = oldCursor;
oldCursor = null;
}
if (options.noShowPopup !== true) {
let popup = this.context.global.popup;
if (popup) {
popup.remove();
this.context.global.popup = null;
}
}
if (options.hoverCallback) {
options.hoverCallback(null);
}
if (oldFeatureId) {
this.map.setFeatureState({ source: sourceId, sourceLayer: layerId, id: oldFeatureId }, { hover: false });
oldFeatureId = null;
}
});
}
}
/**
* 根据给定的点坐标数组直接绘制一个多边形
* @param {[number, number][]} points 多边形的坐标点数组[[x1,y1],[x2,y2],...],参数必填
* @param {object} options 配置参数, 参数可选
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {boolean} options.isFill 是否填充,默认true,参数可选
* @param {number} options.opacity 透明度,默认1,参数可选
* @param {number} options.extrusionHeight 拉伸高度,有拉伸高度为拉伸对象,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Object} 返回绘制的图元对象,返回的对象包含以下属性:
* @example
* // 示例一
* // 根据三个地图范围内随机点创建一个多边形
* let points = this.randomPointInMapBounds(3); // 每个多边形3个点
* points.push(points[0]); // 闭合多边形
* const polygon = this.createPolygon(points, {
* color: vjmap.randomColor(),
* isFill: true,
* opacity: 0.5
* });
* console.log(`多边形 ID:`, polygon.id);
* console.log(`多边形 点坐标:`, polygon.points);
* // 示例二
* // 根据三个地图范围内随机点创建一个多边形,拉伸高度为地图范围宽度的五分之一
* let points2 = this.randomPointInMapBounds(3); // 每个多边形3个点
* points2.push(points2[0]); // 闭合多边形
* const polygon2 = this.createPolygon(points2, {
* color: vjmap.randomColor(),
* isFill: true,
* extrusionHeight: map.getGeoBounds().width() / 5
* });
* console.log(`拉伸多边形 ID:`, polygon2.id);
* console.log(`拉伸多边形 点坐标:`, polygon2.points);
*/
createPolygon(points, options) {
let draw = this.map.getDrawLayer();
let id = vjmap.RandomID(10);
draw.add({
id,
type: 'Feature',
geometry: {
type: options.isFill !== false ? 'Polygon' : 'LineString',
coordinates: options.isFill !== false ? [points.map(pt => this.map.toLngLat(pt))] : points.map(pt => this.map.toLngLat(pt))
},
properties: {
color: options.color,
opacity: options.opacity,
data: options.data,
extrusionHeight: options.extrusionHeight ? this.map.geoLengthToHeight(options.extrusionHeight) : undefined
}
});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.getDrawLayer().delete(id);
});
return {
id,
points
};
}
/**
* 设置地图的倾斜角度
* @param {number} pitch 要设置的倾斜角度
* @example
* this.setPitch(60); // 设置倾斜角度为60度
*/
setPitch(pitch) {
let oldPitch = this.map.getPitch(); // 获取当前倾斜角度
this.map.setPitch(pitch); // 设置新的倾斜角度
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.setPitch(oldPitch);
});
}
/**
* 手动交互式绘制一个点
* @param {object} options 配置参数, 参数可选
* @param {boolean} options.isSnap 是否启用捕捉功能, 参数可选,默认false
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {number} options.size 点大小,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 手动绘制一个点
* const pt = await this.drawPoint(); // 启用捕捉功能采绘制一个点坐标
* if (!pt) return;
* // 点的坐标
* let point = pt.point;
* const x = point[0];
* const y = point[1];
* console.log(x, y);
* // 图元ID
* let id = pt.id;
* console.log(id);
* // 手动绘制一个点,颜色为红色,大小为10,数据值为123,启用捕捉
* const pt2 = await this.drawPoint({
* isSnap: true,
* color: "#FF0000",
* size: 10,
* data: 123
* });
* console.log(pt2);
*/
async drawPoint(options) {
options = options || {};
let point = await this.actionDrawPoint(options.isSnap);
if (!point) return;
return this.createPoint(point, options);
}
/**
* 增加一个信息窗口Popup
* @param {[number, number]} pt 要设置的坐标[x, y]
* @param {Record<string, any>} options 其他参数,参数可选
* @param {any} options.content 弹窗内容,参数可选,默认为空表示不显示弹窗
* @param {[number, number]} options.offset 弹窗偏移量,参数可选,默认为[0, -40]
* @param {boolean} options.showInForm 弹窗内容是否以表单形式显示,参数可 选,默认为false
* @param {boolean} options.isOpen 弹窗内容是否打开,参数可选,默认为false
* @param {string} options.anchor 锚点, 选项包括'center','top','bottom','left','right','top-left','top-right','bottom-left',和'bottom-right'。参数可选
* @param {string} options.maxWidth 弹窗最大宽度,参数可选,默认为'auto'
* @param {string} options.width 弹窗宽度,参数可选
* @param {string} options.height 弹窗高度,参数可选
* @param {string} options.className 弹窗样式类名,参数可选,默认为空
* @param {boolean} options.closeButton 是否显示关闭按钮,参数可选,默认为true
* @param {boolean} options.closeOnClick 是否点击弹窗外部关闭弹窗,参数可选,默认为false
* @param {boolean} options.closeOnMove 是否地图移动时关闭弹窗,参数可选,默认为false
* @param {boolean} options.focusAfterOpen 是否打开弹窗后聚焦弹窗,参数可选,默认为true
* @param {number} options.elevation 坐标标高值,参数可选,默认为0
* @param {boolean} options.visible 是否显示,参数可选,默认为true
* @param {any} options.data 数据内容,参数可选,默认为空
* @returns {string} 返回id
* @example
* // 在坐标[30,50]处添加一个信息窗口,内容为"Hello,World"
* let popupId = this.addPopup([30, 50], {
* content: "Hello,World"
* });
* console.log(popupId);
*/
addPopup(pt, options) {
options = options || {};
let popup = new vjmap.Popup({
offset: options.offset,
anchor: options.anchor,
maxWidth: options.maxWidth,
className: options.className,
closeButton: options.closeButton === undefined ? true : options.closeButton,
closeOnClick: options.closeOnClick,
closeOnMove: options.closeOnMove,
focusAfterOpen: options.focusAfterOpen
})
const dom = this.context.ui.createElementByJsonData(options.content, options.showInForm, {
width: options.width,
height: options.height
})
popup.setDOMContent(dom.element)
popup.id = "popup_" + vjmap.RandomID()
if (options.data) popup.data = options.data;
if (pt) {
popup.setLngLat(this.map.toLngLat(pt));
}
if (options.visible !== false || options.isOpen) {
popup.addTo(this.map);
}
if (options.elevation) {
popup.setHeight(options.elevation);
}
this.context.global.popups = this.context.global.popups || [];
this.context.global.popups.push({id: popup.id, popup: popup, options: options});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deletePopup(popup.id);
});
return popup.id
}
/**
* 切换至栅格瓦片样式
* @example
* // 切换至栅格瓦片样式
* this.switchVectorToRasterStyle();
*/
switchVectorToRasterStyle() {
// 如果当前地图已经使用栅格瓦片样式,则不进行切换
if (!this.map.hasVectorLayer()) {
return
}
this.map.switchVectorToRasterStyle(this.map.getService().rasterTileUrl());
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.switchRasterToVectorStyle(this.map.getService().vectorTileUrl());
});
}
/**
* 创建点符号数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {string} options.iconImage 图标名称,用于绘制图像背景的图像名称
* @param {number} options.iconRotate 图标旋转角度,顺时针旋转,单位为角度,默认0
* @param {[number,number]} options.iconOffset 图标与锚点偏移距离,[x,y]格式,正值向右和向下
* @param {string} options.iconAnchor 图标锚定位置,可选值: "center", "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right"
* @param {string} options.iconRotationAlignment 图标旋转对齐方式,可选值: "map", "viewport", "auto"
* @param {string} options.iconPitchAlignment 地图倾斜时图标方向,可选值: "map", "viewport", "auto"
* @param {boolean} options.iconAllowOverlap 是否允许图标重叠,默认false
* @param {number} options.iconOpacity 图标透明度,0-1之间,默认1
* @param {boolean} options.iconIgnorePlacement 是否忽略碰撞检测,默认false
* @param {boolean} options.iconOptional 图标重叠选项,默认false
* @param {number} options.iconSize 图标缩放比例,默认1
* @param {string} options.iconTextFit 图标适应文本方式,可选值: "none", "width", "height", "both"
* @param {[number,number,number,number]} options.iconTextFitPadding 图标文本匹配padding,[top, right, bottom, left]格式
* @param {number} options.iconPadding 图标边界填充大小,默认1像素
* @param {boolean} options.iconKeepUpright 是否防止图标倒置,默认false
* @param {string} options.iconColor SDF图标颜色,默认"#000000"
* @param {string} options.iconHaloColor SDF图标光晕颜色,默认"rgba(0, 0, 0, 0)"
* @param {number} options.iconHaloWidth 图标光晕宽度,默认0
* @param {number} options.iconHaloBlur 图标光晕模糊度,默认0
* @param {[number,number]} options.iconTranslate 图标平移距离,[x,y]格式
* @param {string} options.iconTranslateAnchor 图标平移参考系,可选值: "map", "viewport"
* @param {string} options.textField 文本内容
* @param {string[]} options.textFont 文本字体,默认["Open Sans Regular","Arial Unicode MS Regular"]
* @param {number} options.textSize 文本大小,默认16
* @param {boolean} options.textAllowOverlap 是否允许文本重叠,默认false
* @param {string} options.textColor 文本颜色,默认"#000000"
* @param {number} options.textOpacity 文本透明度,0-1之间,默认1
* @param {[number,number]} options.textOffset 文本偏移,[x,y]格式
* @param {number} options.textRotate 文本旋转角度
* @param {number} options.textPadding 文本内边距,默认2
* @param {boolean} options.textKeepUpright 是否防止文本倒置,默认true
* @param {number} options.textMaxWidth 文本最大宽度,默认10em
* @param {number} options.textLineHeight 文本行高,默认1.2
* @param {number} options.textLetterSpacing 文字间距,默认0
* @param {string} options.textJustify 文本对齐方式,可选值: "auto", "left", "center", "right"
* @param {string} options.textAnchor 文本锚点位置
* @param {string} options.textRotationAlignment 文本旋转对齐方式
* @param {string} options.textPitchAlignment 文本倾斜对齐方式
* @param {number} options.textMaxAngle 相邻字符最大角度,默认45°
* @param {string[]} options.textWritingMode 文本书写模式
* @param {string} options.textTransform 文本转换方式,可选值: "none", "uppercase", "lowercase"
* @param {number} options.textRadialOffset 文本径向偏移
* @param {string[]} options.textVariableAnchor 文本可变锚点位置数组
* @param {boolean} options.textIgnorePlacement 是否忽略文本位置
* @param {boolean} options.textOptional 文本显示选项
* @param {string} options.textHaloColor 文本光晕颜色
* @param {number} options.textHaloWidth 文本光晕宽度
* @param {number} options.textHaloBlur 文本光晕模糊度
* @param {[number,number]} options.textTranslate 文本平移距离
* @param {string} options.textTranslateAnchor 文本平移参考系
* @param {string} options.symbolPlacement 符号放置位置,可选值: "point", "line", "line-center"
* @param {number} options.symbolSpacing 符号间距,默认250
* @param {boolean} options.symbolAvoidEdges 是否避免边缘碰撞
* @param {string} options.symbolZOrder 符号绘制顺序,可选值: "auto", "viewport-y", "source"
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer24的点符号数据图层, 图标名称为_markerIcon_
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createSymbolDataLayer(geojson, {
* layerName: 'layer24',
* iconImage: '_markerIcon_'
* });
* console.log(layer);
*/
async createSymbolDataLayer(data, options) {
options = options || {};
let type = "symbol";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
if (options.textField && data.features.length > 0 && data.features[0].properties && (options.textField in data.features[0].properties)) {
// 如果内容是geojson中的一个属性,则需要获取该属性值
options.textField = "['get', '" + options.textField + "']";
}
let layerOptions = {
layerId,
sourceId,
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createSymbolDataLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 根据给定的坐标位置直接绘制一个矩形
* @param {[number, number, number, number]} points 矩形的坐标[x1,y1,x2,y2],表示左下角和右上角坐标,参数必填
* @param {object} options 配置参数, 参数可选
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {boolean} options.isFill 是否填充,默认false,参数可选
* @param {number} options.opacity 透明度,默认1,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Object} 返回绘制的图元对象,返回的对象包含以下属性:
* @example
* // 创建三个地图范围内随机位置的矩形, 矩形宽高为地图宽度的1/20,颜色随机,填充,透明度0.5, 其中第二个不填充
* let points = this.randomPointInMapBounds(3);
* let rect = this.getMapGeoBounds(); // 获取地图范围
* let width = (rect[2] - rect[0]) / 20; // 矩形宽度为地图宽度的1/20
* for(let i = 0; i < points.length; i++) {
* const rect = this.createRectangle([
* points[i][0], points[i][1],
* points[i][0] + width, points[i][1] + width
* ], {
* color: vjmap.randomColor(),
* isFill: i == 1 ? false : true,
* opacity: 0.5,
* data: i
* });
* console.log(`矩形${i+1} ID:`, rect.id);
* console.log(`矩形${i+1} 坐标:`, rect.points);
* }
*/
createRectangle(points, options) {
let bounds = vjmap.GeoBounds.fromArray(points);
let pts = bounds.toPointArray();
pts.push(pts[0]);
let draw = this.map.getDrawLayer();
let id = vjmap.RandomID(10);
draw.add({
id,
type: 'Feature',
geometry: {
type: options.isFill ? 'Polygon' : 'LineString',
coordinates: options.isFill ? [pts.map(pt => this.map.toLngLat(pt))] : pts.map(pt => this.map.toLngLat(pt))
},
properties: {
color: options.color,
opacity: options.opacity,
data: options.data
}
});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.getDrawLayer().delete(id);
});
return {
id,
points
};
}
/**
* 保存用户自定义数据至服务端
* @param {any} value 键值
* @param {any} prop 属性值
* @param {number} ttl 有效时间,单位秒,默认长期有效
* @returns {Promise<boolean>} 返回保存是否成功, true表示成功,false表示失败
* @example
* // 保存用户自定义数据至服务端,键名为testKey,值为testValue,属性为{ prop1: "value1" }
* await this.saveCustomDataServer("testKey", "testValue", { prop1: "value1" });
* // 批量保存用户自定义数据至服务端,键名为testKey1,值为testValue1,属性为{ prop1: "value1" },有效时间为3600秒,键名为testKey2,值为testValue2,属性为{ prop2: "value2" },有效时间为7200秒
* await this.saveCustomDataServer([
* { key: "testKey1", value: "testValue1", prop: { prop1: "value1" }, ttl: 3600 },
* { key: "testKey2", value: "testValue2", prop: { prop2: "value2" }, ttl: 7200 }
* ]);
*/
async saveCustomDataServer(key, value, prop, ttl) {
let svc = this.map.getService();
let res = await svc.saveCustomData(key, value, prop, ttl);
return res?.status === "ok";
}
/**
* 增加地图比例尺控件
* @param {number} options.maxWidth 比例尺最大宽度,参数可选
* @param {string} options.unit 比例尺单位,参数可选
* @param {'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'} options.position 控件位置,参数可选, 默认'bottom-left'
* @example
* // 增加地图比例尺控件,位置右下角
* this.addMapScaleControl({ position: 'bottom-right' });
*/
addMapScaleControl(options) {
options = options || {};
this.context.global.controls = this.context.global.controls || {};
if (this.context.global.controls.scaleControl) {
this.map.removeControl(this.context.global.controls.scaleControl.control);
}
let scaleControl = new vjmap.ScaleControl(options);
this.map.addControl(scaleControl, options.position || 'bottom-left');
this.context.global.controls.scaleControl = {
control: scaleControl,
position: options.position,
options: options
};
// 增加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.removeControl(scaleControl);
delete this.context.global.controls.scaleControl;
});
}
/**
* 增加一个功能按钮及实现功能
* @param {string} name 按钮名,必填
* @param {Function} func 要实现的功能函数,必填
* @param {string} tooltip 按钮的提示信息, 参数可选
* @example
* // 增加一个功能按钮,按钮名为"放大一级",功能为将地图缩放级别放大一级
* this.addButtonItem("放大一级", () => {
* let zoom = map.getZoom();
* map.setZoom(zoom + 1);
* })
*/
addButtonItem(name, func, tooltip) {
this.context.ui.addButtonItem(name, func, tooltip);
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.removeButtonItem(name);
});
}
/**
* 删除绘制的图元
* @param {string | string[]} id 图元ID或图元ID数组,参数必填
* @example
* // 删除绘制的全部图元
* const geojson = this.getDrawAll();
* if (!geojson) return;
* let ids = [];
* for(let i = 0; i < geojson.features.length; i++) {
* let feature = geojson.features[i];
* ids.push(feature.id);
* }
* this.deleteDrawFeature(ids);
*/
deleteDrawFeature(id) {
let draw = this.map.getDrawLayer();
draw.delete(id);
}
/**
* 创建线拉伸图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {string} options.fillExtrusionColor 拉伸颜色,参数可选,默认为"#00ffff"
* @param {number} options.fillExtrusionOpacity 透明度,参数可选,取值范围0-1
* @param {number} options.fillExtrusionHeight 拉伸CAD图制图单位,参数可选,默认为500000
* @param {number} options.fillExtrusionBase 拉伸底部高度,单位CAD图制图单位,参数可选,默认为0
* @param {string} options.fillExtrusionPattern 拉伸填充图案,参数可选,使用雪碧图中的图像名称,图像宽度必须是2的系数
* @param {[number,number]} options.fillExtrusionTranslate 平移量,参数可选,格式[x,y],负数表示左和上,单位像素,默认[0,0]
* @param {string} options.fillExtrusionTranslateAnchor 平移参考系,参数可选,"map"表示相对地图平移,"viewport"表示相对视窗平移
* @param {boolean} options.fillExtrusionVerticalGradient 是否应用垂直渐变,参数可选,true时侧面下方位置会略暗
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {number} options.offsetLine 线扩展大小,参数可选,默认为10
* @param {boolean} options.isBreakLine 是否打散成线段,参数可选
* @param {number} options.anmiDuration 动画持续时长,单位秒,参数可选,默认为0(表示没有动画)
* @param {number} options.anmiRepeat 动画重复次数,参数可选,默认为0(表示无限循环)
* @param {string} options.anmiRepeatType 动画重复类型,参数可选,可选值:"reverse"(反向)、"loop"(循环)、"mirror"(镜像)
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer18的线拉伸数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createLineExtrusionsLayer(geojson, {
* layerName: 'layer18'
* });
* console.log(layer);
*/
async createLineExtrusionsLayer(data, options) {
options = options || {};
let type = "lineExtrusions";
if(options.fillExtrusionHeight) {
options.fillExtrusionHeight = this.map.geoLengthToHeight(options.fillExtrusionHeight);
}
if(options.fillExtrusionBase) {
options.fillExtrusionBase = this.map.geoLengthToHeight(options.fillExtrusionBase);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
fillExtrusionColor: "#00ffff",
fillExtrusionHeight: this.map.geoLengthToHeight(this.map.getGeoBounds().width() / 6),
anmiDuration: 0,
offsetLine: 10,
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createLineExtrusionsLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 叠加CAD图
* @param {string} [options.mapid] 地图ID
* @param {string} [options.version] 地图版本(为空时采用当前打开的地图版本)
* @param {string} [options.layer] 图层样式名称(如果为空时,则用默认样式)
* @param {string} [options.epsg] cad图epsg代号
* @param {"beijing54"|"xian80"|"cgs2000"} [options.crs] cad图的坐标系,beijing54(北京54坐标)、xian80(西安80坐标、cgs2000(CGCS2000坐标)
* @param {string} [options.webMapTiles] 叠加地图为互联网地图时,数据瓦片地址, 可为空
* @param {boolean} [options.overMapImage] 叠加地图为互联网地图时,是否为影像地图
* @param {boolean} [options.isSetRotateZero] 公共点叠加时是否允许旋转
* @param {boolean} [options.baseMapWgs84] 底图是否为互联网地图wgs84坐标
* @returns {Promise<string>} 返回叠加的图层名称
* @example
* // 示例一
* // 叠加cad图,mapid为sys_world
* let layerName = await this.overlayCadMap({
* mapid: "sys_world"
* });
* console.log("layerName", layerName);
* // 示例二
* // 自动叠加cad图,mapid为sys_cad2000
* let layerName2 = await this.overlayCadMap({
* mapid: "sys_cad2000",
* overlayType: "auto"
* });
* console.log("layerName2", layerName2);
* // 示例三
* // 直接叠加cad图,mapid为sys_world
* let layerName3 = await this.overlayCadMap({
* mapid: "sys_world",
* overlayType: "direct"
* });
* console.log("layerName3", layerName3);
*/
async overlayCadMap(options) {
options = {...options}
// 判断底图是否为互联网地图
let isWebMap = this.map.getMapProjection() instanceof vjmap.LnglatProjection;
let wmsTileUrl;
if (!options.mapid) {
this.showMessage("请设置要叠加的cad图的mapid");
return;
}
if (isWebMap) {
// 底图是互联网地图,互联网地图叠加cad图, 只能自动叠加或四参数叠加
let epsg = options.epsg;
if (!epsg) {
epsg = await this.getCadEpsgCode(options);
}
if (options.overlayType !== "param" && epsg) {
options.epsg = epsg;
// 自动叠加
wmsTileUrl = await this.getWmsTileUrl(options);
let svc = this.map.getService();
// 把cad图的坐标转成互联网地图的坐标,把地图移动到cad图的中心点
const cadToWebCoordinate = async point => {
let result = await svc.cmdTransform(epsg, "EPSG:4326", vjmap.geoPoint(point)); // 如果不想调服务转,也可以在前端用proj4库
return result[0];// 返回的第1条结果
}
let metadata = await svc.metadata(options.mapid, options.version);
let mapExtent = vjmap.GeoBounds.fromString(metadata.bounds);
let p1 = await cadToWebCoordinate(mapExtent.min);
let p2 = await cadToWebCoordinate(mapExtent.max);
this.fitMapBounds([p1[0],p1[1],p2[0],p2[1]], {padding: 0});
} else {
// 四参数叠加
// 用四参数叠加, 左边是默认地图,右边是要叠加的cad图
let fp = await this.context.ui.getFourParamter({
mapid: options.mapid,
version: options.version,
layer: options.layer
});
if (!fp) return;
options.coordinates = [];
for(let n = 0; n < fp.leftMarkers.length; n++) {
let leftMarker = fp.leftMarkers[n];
let rightMarker = fp.rightMarkers[n];
options.coordinates.push({
x1: rightMarker.x,
y1: rightMarker.y,
x2: leftMarker.x,
y2: leftMarker.y
});
}
options.overlayType = "param";
options.isSetRotateZero = !fp.allowsRotation;
wmsTileUrl = await this.getWmsTileUrl(options);
let svc = this.map.getService();
// 把cad图的坐标转成互联网地图的坐标,把地图移动到cad图的中心点
const cadToWebCoordinate = point => {
let p = vjmap.coordTransfromByFourParamter(point,{
dx: fp.fourParamter[0],
dy: fp.fourParamter[1],
scale: fp.fourParamter[2],
rotate: fp.fourParamter[3],
});
let wgs84 = vjmap.Projection.mercator2LngLat(p); // 墨卡托3857转经纬度wgs84
return wgs84;
}
let metadata = await svc.metadata(options.mapid, options.version);
let mapExtent = vjmap.GeoBounds.fromString(metadata.bounds);
let p1 = cadToWebCoordinate(mapExtent.min);
let p2 = cadToWebCoordinate(mapExtent.max);
this.fitMapBounds([p1[0],p1[1],p2[0],p2[1]], {padding: 0});
}
} else {
// 底图是cad图,cad图叠加cad图,只能直接通过坐标叠加或四参数叠加
if (options.overlayType == "direct") {
// 直接通过坐标叠加
// 自动叠加
wmsTileUrl = await this.getWmsTileUrl(options);
} else {
// 四参数叠加
// 用四参数叠加, 左边是默认地图,右边是要叠加的cad图
if (!options.mapid) {
this.showMessage("请设置要叠加的cad图的mapid");
return;
}
let fp = await this.context.ui.getFourParamter({}, {
mapid: options.mapid,
version: options.version,
layer: options.layer
});
if (!fp) return;
options.coordinates = [];
for(let n = 0; n < fp.leftMarkers.length; n++) {
let leftMarker = fp.leftMarkers[n];
let rightMarker = fp.rightMarkers[n];
options.coordinates.push({
x1: leftMarker.x,
y1: leftMarker.y,
x2: rightMarker.x,
y2: rightMarker.y
});
}
options.overlayType = "param";
options.overMapType = "CAD";
options.isSetRotateZero = !fp.allowsRotation;
wmsTileUrl = await this.getWmsTileUrl(options);
}
}
if (!wmsTileUrl) return;
// 创建栅格瓦片图层
let layer = await this.createRasterLayer({
tiles: wmsTileUrl,
});
return layer
}
/**
* 将CAD图几何坐标转换为地图像素点坐标
* @returns {[number, number]} 返回包含两个数字的数组[x,y],表示地图像素点坐标
* @example
* // 将CAD图几何坐标[1000, 2000]转换为地图像素点坐标
* let pixel = this.geoPointToPixelPoint([1000, 2000]);
* let x = pixel[0];
* let y = pixel[1];
* console.log(x, y);
*/
geoPointToPixelPoint(pt) {
let lngLat = this.map.toLngLat(pt);
let pixelPoint = this.map.project(lngLat);
return [pixelPoint.x, pixelPoint.y];
}
/**
* 切换至矢量瓦片样式
* @example
* // 切换至矢量瓦片样式
* this.switchRasterToVectorStyle();
*/
switchRasterToVectorStyle() {
// 如果当前地图已经使用矢量瓦片样式,则不进行切换
if (this.map.hasVectorLayer()) {
return
}
this.map.switchRasterToVectorStyle(this.map.getService().vectorTileUrl());
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.switchVectorToRasterStyle(this.map.getService().rasterTileUrl());
});
}
/**
* 获取所有信息窗口Popup
* @param {string} id 信息窗口Popup的id
* @param {[number, number]} position 信息窗口Popup的坐标[x, y]
* @param {string} data 信息窗口Popup的数据
* @returns {Array<Object>} 返回所有信息窗口Popup, 每个Popup对象包含:
* @example
* // 获取所有信息窗口Popup
* let popups = this.getAllPopups();
* for(let i = 0; i < popups.length; i++) {
* let popup = popups[i];
* let x = popup.position[0];
* let y = popup.position[1];
* console.log(popup.id, x, y, popup.data);
* }
*/
getAllPopups() {
let popups = this.context.global.popups || [];
popups = popups.filter(m => m.id !== undefined);
return popups.map(m => {
let popup = m.popup;
let point = this.map.fromLngLat(popup.getLngLat());
return {
id: m.id,
position: [point.x, point.y],
data: popup.data
}
});
}
/**
* 比较地图不同
* @param {Object} options 参数
* @param {string} options.mapid1 地图一ID,可选,如为空时,则使用当前打开的地图
* @param {string} [options.version1] 地图一版本(为空时采用当前打开的地图版本)
* @param {string} [options.layer1] 地图一样式图层,可选,如为空时,则使用当前打开的地图样式名称
* @param {string} options.mapid2 地图二ID,可选,如为空时,则使用当前打开的地图
* @param {string} [options.version2] 地图二版本(为空时采用当前打开的地图版本)
* @param {string} [options.layer2] 地图二样式图层,可选,如为空时,则使用当前打开的地图样式名称
* @param {boolean} [options.noCompareNew] 不比较新增部分
* @param {boolean} [options.noCompareDelete] 不比较删除部分
* @param {number} [options.size] 比较大小. 默认 4096
* @param {number} [options.cellsize] 比较单元大小. 默认 10
* @param {number} [options.diffMinPixel] 不同的像素最小值. 默认 0
* @param {number} [options.diffMinAlpha] 不同的透明最小值. 默认 25
* @param {number} [options.diffMinColor] 不同的颜色最小值. 默认 6
* @returns {Promise<Object>} 返回比较结果, 如果失败则返回undefined, 如果成功则返回字段如下:
* @example
* // 示例
* // 比较当前地图和sys_hello的不同
* let res = await this.findMapDiff({
* mapid2: "world"
* });
* console.log(res);
*/
async findMapDiff(options) {
options = options || {};
let svc = this.map.getService();
options.mapid1 = options.mapid1 || svc.currentMapParam().mapid;
options.mapid2 = options.mapid2 || svc.currentMapParam().mapid;
let res = await svc.cmdMapDiff(options);
if (res && res.status) {
const toGeoJSON = (points) => {
let geojson = {
type: "FeatureCollection",
features: []
};
if (!points || points.length === 0) return geojson;
points.forEach(p => {
if (p.length > 0) {
p.push(p[0]);
}
geojson.features.push({
type: "Feature",
geometry: {
type: "LineString",
coordinates: p
}
});
}) ;// 闭合
return geojson;
}
let result = {
new: toGeoJSON(res.new),
modify: toGeoJSON(res.modify),
del: toGeoJSON(res.del),
diff: toGeoJSON([...res.new, ...res.modify, ...res.del])
};
return result;
}
return undefined;
}
/**
* 设置数据图层属性
* @param {string} layerName 图层名称,参数必填
* @param {object} property 图层属性对象,参数必填
* @example
* // 设置图层名称为layer1的数据图层属性为{"minZoom":1,"maxZoom":5}
* let layerName = 'layer1';
* let property = {"minZoom":1,"maxZoom":5};
* this.setDataLayerProperty(layerName, property);
*/
setDataLayerProperty(layerName, property) {
let item = this.context.global.layers.find(layer => layer.layerName == layerName);
if(item) {
let oldProp = {...vjcommon.toMapLayer(item.layer, item.layer.mapLayer)};
let layerId = item.layerId;
if (item.layer && typeof(item.layer.getLayerId) == "function") {
layerId = item.layer.getLayerId();
}
item.layer.setLayerStyle(this.map, layerId, property, oldProp);
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.setDataLayerProperty(item.layerName, oldProp);
});
}
}
/**
* 选择图中实体对象
* @param {object} options 选择实体对象的选项,参数可选
* @param {boolean} options.useGeomCoord 使用几何坐标还是位置坐标,false表示位置坐标,true表示几何坐标,参数可选, 默认几何坐标true
* @param {boolean} options.includeWholeEntity 选择时是否选择上整个实体对象,true表示选择上整个实体对象,false表示选择上实体对象的一部分,参数可选, 默认false
* @param {boolean} options.isPointSel 点选还是框选,false表示框选,true表示点选,参数可选, 默认false
* @param {boolean} options.disableSnap 是否禁用捕捉,false表示不禁用捕捉,true表示禁用捕捉,参数可选, 默认false
* @param {number} options.maxSelectCount 最大选择数量,参数可选, 默认不限制
* @returns {Object} 返回标准的GeoJson格式的FeatureCollection对象, 包含features属性, 每个feature包含geometry和properties属性,properties属性中包含如下字段:
* @example
* // 示例一
* // 点击选择图中实体对象
* const geojson1 = await this.selectFeatures({
* isPointSel: true
* });
* // 输出选择的实体objectid
* let selectedObjectids = [];
* for (let feature of geojson1.features) {
* selectedObjectids.push(feature.properties.objectid);
* }
* console.log(selectedObjectids);
* // 示例二
* // 框选图中实体对象, 使用位置坐标, 选择整个实体对象
* const geojson2 = await this.selectFeatures({
* useGeomCoord: false,
* includeWholeEntity: true,
* isPointSel: false
* });
* // 输出选择实体的个数
* console.log(geojson2.features.length);
* // 示例三
* // 点击选择图中的实体对象,选择整个实体对象, 最多选择1个
* const geojson3 = await this.selectFeatures({
* isPointSel: true,
* includeWholeEntity: true,
* maxSelectCount: 1
* });
* console.log(geojson3);
*/
async selectFeatures(options) {
options = options || {};
let features = await vjcommon.selectFeatures(this.map, options.useGeomCoord !== false, options.includeWholeEntity, options.isPointSel, options.disableSnap, undefined, options.maxSelectCount);
return {
features: features || [],
type: "FeatureCollection"
}
}
/**
* 拆分子图
* @returns {Promise<string[]>} 返回拆分后的每个子图的fileid (数组次序与传入的子图范围数组clipBounds次序一致), 如果失败则返回undefined
* @example
* // 自动提取子图范围,获取返回的子图范围数组,拆分子图
* let res = await this.findChildMapRects();
* if (!res) return;
* let fileids = await this.splitChildMaps({
* clipBounds: res.childMapBounds
* });
* console.log(fileids);
*/
async splitChildMaps(options) {
options = options || {};
let svc = this.map.getService();
if (!options.clipBounds || (options.clipBounds && options.clipBounds.length == 0)) {
if (this.map.logInfo) this.map.logInfo('参数范围为空', "error", 10000);
return;
}
for(let n = 0; n < options.clipBounds.length; n++) {
if (Array.isArray(options.clipBounds[n]) && options.clipBounds[n].length == 4) {
options.clipBounds[n] = options.clipBounds[n].join(',');
}
}
let result = await svc.cmdSplitChildMaps({
// 方法 cloneObjects 通过深度克隆实体,效率最快(默认), cloneDb 通过克隆文档数据库,效率较快;cloneMap 通过克隆图,效率较慢,但最能保持原样
method: "cloneObjects",
// 是否启动新进程处理(不影响主进程,看初始速度慢些) 默认true
startNewProcess: true,
// 拆分后的图是否全图 默认false
isFullExtent: true,
...options
});
if (result && result.error) {
if (this.map.logInfo) this.map.logInfo(result.error, "error", 10000);
return;
}
let fileids = result.fileids;
if (fileids && fileids.length > 0) {
for(let n = 0; n < fileids.length; n++) {
// 去掉文件后缀
fileids[n] = fileids[n].split(".")[0];
}
}
return fileids;
}
/**
* 在地图范围内随机生成点
* @param {number} count 要生成的点数,默认1
* @param {number} boundsType 范围类型(0 地图绘制范围,1 地图实际范围, 2 地图整个范围),默认0, 参数可选
* @returns {[number, number][]} 返回包含多个坐标点的数组[[x1,y1],[x2,y2],...]
* @example
* // 示例一
* // 在地图的范围内随机生成1个点
* let points = this.randomPointInMapBounds(1);
* let x = points[0][0];
* let y = points[0][1];
* console.log(x, y);
* // 示例二
* // 在地图的实际范围内随机生成10个点
* let points2 = this.randomPointInMapBounds(10, 1);
* for (let i = 0; i < points2.length; i++) {
* let x = points2[i][0];
* let y = points2[i][1];
* console.log(x, y);
* }
*/
randomPointInMapBounds(count, boundsType) {
let rect = this.getMapGeoBounds(boundsType);
let geoBounds = vjmap.GeoBounds.fromArray(rect);
let points = geoBounds.randomPoints(count, count);
return points.map(p => [p.x, p.y]);
}
/**
* 采集一个斜矩形的坐标
* @param {boolean} isSnap 是否启用捕捉功能, 默认false
* @returns {Promise<number[][] | undefined>} 返回包含四个点坐标的数组[[x1,y1],[x2,y2],[x3,y3],[x4,y4]],如果取消操作则返回undefined
* @example
* const rect = await this.actionDrawSlantRectangle();
* if (!rect) return;
* console.log(rect); // 斜矩形的四个点坐标
*/
async actionDrawSlantRectangle(isSnap) {
this.context.tools.logInfo("请在地图绘一个斜矩形")
let snapObj = {};
if (isSnap) {
vjcommon.getMapSnapPoints(this.map, snapObj);
}
let drawRect = await vjmap.Draw.actionDrawSlantRectangle(this.map, isSnap ? {
api: {
getSnapFeatures: snapObj
}
} : {});
if (drawRect.cancel) {
return;
}
let coords = drawRect.features[0].geometry.coordinates[0];
return coords.map(c => {
let p = this.map.fromLngLat(c);
return [p.x, p.y];
});
}
/**
* 创建箭头线数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {string} options.lineColor 线条颜色,默认#FF9900
* @param {number} options.lineWidth 线宽,默认10
* @param {number} options.lineOpacity 线透明度,取值0-1
* @param {number} options.lineGapWidth 线间隙宽度,在线的实际路径之外绘制描边时的内部间隙宽度
* @param {number} options.lineOffset 线的偏移量,正值向右偏移,负值向左偏移
* @param {number} options.lineBlur 线模糊度,需小于线宽度
* @param {string} options.strokeStyle 线型,'solid'实线或'dashed'虚线
* @param {[number,number]} options.lineDasharray 虚线样式,格式如[1,1]表示实线虚线的组合
* @param {[number,number]} options.lineTranslate 平移量,格式[x,y],负数分别表示左和上
* @param {string} options.lineCap 线帽形状,'butt'尖头、'round'圆头、'square'平头
* @param {string} options.lineJoin 拐角形状,'bevel'平拐、'round'圆拐、'miter'棱拐
* @param {string} options.strokeImage 线图案,需与预加载的图片名对应
* @param {string} options.lineGradient 线渐变
* @param {boolean} options.showBorder 是否显示线边框,默认true
* @param {string} options.borderColor 边框颜色,默认#f00
* @param {number} options.borderWidth 边框宽度
* @param {number} options.borderOpacity 边框透明度,取值0-1
* @param {number} options.borderGapWidth 边框间隙宽度
* @param {number} options.borderOffset 边框偏移量
* @param {number} options.borderBlur 边框模糊度
* @param {string} options.borderGradient 边框渐变
* @param {boolean} options.showDir 是否显示箭头,默认true
* @param {number} options.dirSize 箭头大小
* @param {number} options.dirSpacing 箭头间距
* @param {number} options.dirImageWidth 箭头图像宽度
* @param {number} options.dirImageHeight 箭头图像高度
* @param {string} options.dirImageColor 箭头图像颜色
* @param {string} options.dirIconColor 箭头图标颜色
* @param {string} options.dirLayout 箭头布局
* @param {string} options.cursor 鼠标光标形状
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer5的箭头线数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createArrowLineLayer(geojson, {
* layerName: 'layer5'
* });
* console.log(layer);
*/
async createArrowLineLayer(data, options) {
options = options || {};
let type = "arrowLine";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
lineWidth: 10,
showDir: true,
showBorder: true,
borderColor: "#f00",
lineColor: '#FF9900',
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createArrowLineLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 创建多边形拉伸动画数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {string} options.fillExtrusionColor 拉伸颜色,参数可选,默认为"#00ffff"
* @param {number} options.fillExtrusionOpacity 透明度,参数可选,取值范围0-1
* @param {number} options.fillExtrusionHeight 拉伸高度,单位CAD图制图单位,参数可选,默认为500000
* @param {number} options.fillExtrusionBase 拉伸底部高度,单位CAD图制图单位,参数可选,默认为0
* @param {string} options.fillExtrusionPattern 拉伸填充图案,参数可选,使用雪碧图中的图像名称,图像宽度必须是2的系数
* @param {[number,number]} options.fillExtrusionTranslate 平移量,参数可选,格式[x,y],负数表示左和上,单位像素,默认[0,0]
* @param {string} options.fillExtrusionTranslateAnchor 平移参考系,参数可选,"map"表示相对地图平移,"viewport"表示相对视窗平移
* @param {boolean} options.fillExtrusionVerticalGradient 是否应用垂直渐变,参数可选,true时侧面下方位置会略暗
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {number} options.anmiDuration 动画持续时长,单位秒,参数可选,默认为2,值为0表示没有动画
* @param {number} options.anmiRepeat 动画重复次数,参数可选,值为0表示无限循环
* @param {string} options.anmiRepeatType 动画重复类型,参数可选,可选值:"reverse"(来回播放)、"loop"(循环播放)、"mirror"(镜像播放)
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer13的多边形拉伸动画数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createFillExtrusionsAnimateLayer(geojson, {
* layerName: 'layer13'
* });
* console.log(layer);
*/
async createFillExtrusionsAnimateLayer(data, options) {
options = options || {};
let type = "fillExtrusionsAnimate";
if(options.fillExtrusionHeight) {
options.fillExtrusionHeight = this.map.geoLengthToHeight(options.fillExtrusionHeight);
}
if(options.fillExtrusionBase) {
options.fillExtrusionBase = this.map.geoLengthToHeight(options.fillExtrusionBase);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
fillExtrusionColor: "#00ffff",
fillExtrusionHeight: this.map.geoLengthToHeight(this.map.getGeoBounds().width() / 6),
anmiDuration: 2,
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createFillExtrusionsAnimateLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 采集一个多边形的坐标
* @param {boolean} isSnap 是否启用捕捉功能, 默认false
* @returns {Promise<number[][] | undefined>} 返回坐标点数组[[x1,y1],[x2,y2],...],如果取消操作则返回undefined
* @example
* const polygon = await this.actionDrawPolygon();
* if (!polygon) return;
* // 输出多边形的所有点坐标
* for (let i = 0; i < polygon.length; i++) {
* let x = polygon[i][0];
* let y = polygon[i][1];
* console.log(x, y);
* }
*/
async actionDrawPolygon(isSnap) {
this.context.tools.logInfo("请在地图上绘制一个多边形")
let snapObj = {};
if (isSnap) {
vjcommon.getMapSnapPoints(this.map, snapObj);
}
let drawPolygon = await vjmap.Draw.actionDrawPolygon(this.map, isSnap ? {
api: {
getSnapFeatures: snapObj
}
} : {});
if (drawPolygon.cancel) {
return;
}
let coords = drawPolygon.features[0].geometry.coordinates[0];
return coords.map(c => {
let p = this.map.fromLngLat(c);
return [p.x, p.y];
});
}
/**
* 设置点标记Marker的属性
* @param {string} id 点标记Marker的id
* @param {Record<string, any>} options 其他参数,参数可选
* @param {[number, number]} options.position 坐标[x, y],参数可选,默认为undefined
* @param {string} options.color 颜色,参数可选,默认为#3FB1CE
* @param {any} options.popupContent 弹窗内容,参数可选,默认为空表示不显示弹窗
* @param {boolean} options.popupShowInForm 弹窗内容是否以表单形式显示,参数可选,默认为false
* @param {number} options.popupWidth 弹窗宽度,参数可选
* @param {number} options.popupHeight 弹窗高度,参数可选
* @param {boolean} options.popupOpen 弹窗内容是否打开,参数可选,默认为false
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {number} options.rotation 旋转角度,参数可选,默认为0
* @param {string} options.icon 自定义图标名称,参数可选,默认为空
* @param {any} options.data 数据内容,参数可选,默认为undefined
* @param {number} options.elevation 坐标标高值,参数可选,默认为0
* @param {boolean} options.visible 是否显示,参数可选,默认为true
* @example
* // 设置id为marker_1的点标记的属性,坐标为[10, 20],颜色为红色,弹窗内容为"Hello, World!",图标为sensor,可拖动,旋转角度为45度,数据内容为{name: "Sensor", value: 100}
* this.setMarkerProperty("marker_1", {
* position: [10, 20],
* color: "#FF0000",
* popupContent: "Hello, World!",
* icon: "sensor",
* draggable: true,
* rotation: 45,
* data: {name: "Sensor", value: 100}
* });
*/
setMarkerProperty(id, options) {
let markers = this.map.getMarkers();
let marker = markers.find(m => m.id == id);
let oldOptions = {};
if (marker) {
if (options.position) {
let position = this.map.fromLngLat(marker.getLngLat());
oldOptions.position = [position.x, position.y];
marker.setLngLat(this.map.toLngLat(options.position));
}
if (options.color) {
oldOptions.color = marker._color;
marker.setColor(options.color);
}
if (options.data !== undefined) {
oldOptions.data = marker.data;
marker.data = options.data;
}
if (options.elevation) {
oldOptions.elevation = marker.getHeight();
marker.setHeight(options.elevation);
}
if (options.popupContent) {
let popup = marker.getPopup();
if (!popup) {
popup = new vjmap.Popup({
offset: options.popupOffset || [0, -40]
})
marker.setPopup(popup)
}
const dom = this.context.ui.createElementByJsonData(options.popupContent, options.popupShowInForm, {
width: options.popupWidth,
height: options.popupHeight
})
popup.setDOMContent(dom.element)
}
if (options.popupOpen !== undefined) {
let popup = marker.getPopup();
if (popup) {
if (popup.isOpen() && !options.popupOpen) {
marker.togglePopup()
} else if (!popup.isOpen() && options.popupOpen) {
marker.togglePopup()
}
}
}
if (options.icon) {
// 获取自定义图标数据, 在工具栏中的"地图图像管理"中对自定义图标进行管理
let image = this.context.ui.getCustomImages(options.icon);
if (image) {
let el = marker.getElement();
if (el) {
el.className = 'marker';
let imgValue = image.value;
if (imgValue.toLowerCase().indexOf("<svg") == 0) {
//svg或base64 png
imgValue = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(imgValue))) ;
}
el.style.backgroundImage =
`url("${imgValue}")`;
el.style.width = (image.width ?? 40) + 'px';
el.style.height = (image.height ?? 40) + 'px';
el.style.backgroundSize = '100%';
}
}
}
if (options.draggable) {
oldOptions.draggable = marker.isDraggable();
marker.setDraggable(options.draggable);
}
if (options.rotation) {
oldOptions.rotation = marker.getRotation();
marker.setRotation(options.rotation);
}
if (options.visible !== undefined) {
oldOptions.visible = marker.getElement().style.display !== 'none';
marker.setVisible(options.visible, true);
}
}
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.setMarkerProperty(id, oldOptions);
});
}
/**
* 获取所有点标记Marker
* @param {string} id 点标记Marker的id
* @param {[number, number]} position 点标记Marker的坐标[x, y]
* @param {string} data 点标记Marker的数据
* @returns {Array<Object>} 返回所有点标记Marker, 每个Marker对象包含:
* @example
* // 获取所有点标记Marker
* let markers = this.getAllMarkers();
* for(let i = 0; i < markers.length; i++) {
* let marker = markers[i];
* let x = marker.position[0];
* let y = marker.position[1];
* console.log(marker.id, x, y, marker.data);
* }
*/
getAllMarkers() {
let markers = this.map.getMarkers();
markers = markers.filter(m => m.id !== undefined);
return markers.map(m => {
let point = this.map.fromLngLat(m.getLngLat());
return {
id: m.id,
position: [point.x, point.y],
data: m.data
}
});
}
/**
* 缩放至地图范围
* @param {Object} [options] 缩放选项
* @param {boolean} [options.linear] 如果true,则地图使用Map#easeTo进行转换。如果false,则地图使用Map#flyTo进行转换
* @param {Function} [options.easing] 动画过渡的缓动函数
* @param {Array<number>} [options.offset] 相对于地图中心的给定边界的中心,以像素为单位(默认[0,0])
* @param {number} [options.maxZoom] 当地图视图转换到指定边界时允许的最大缩放级别
* @param {boolean} [options.fitFillClip] 默认为窗口包含地图,如果要使地图占满窗口超出的部分裁剪,请设置为true
* @param {Object} [eventData] 要添加到此方法触发的事件的事件对象的附加属性
* @param {boolean} [options.isFlash] 是否使用闪烁动画,默认false
* @example
* // 示例一
* // 缩放至地图范围[0,0,10,10],并添加10像素的填充
* this.fitMapBounds([0,0,10,10], {padding: 10});
* // 示例二
* // 缩放至全图显示
* this.fitMapBounds();
*/
fitMapBounds(bounds, options, eventData) {
options = options || {}
if (Array.isArray(bounds)) {
bounds = vjmap.GeoBounds.fromArray(bounds);
}
let oldBounds = this.map.fromLngLat(this.map.getBounds()); // 获取当前地图范围
this.map.fitMapBounds(bounds, options, eventData) // 缩放至地图范围
if (options.isFlash === true) {
__flashBounds(typeof bounds === 'number' ? this.map.getGeoBounds(bounds) : bounds);
}
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.fitMapBounds(oldBounds);
});
}
/**
* 获取用户数据
* @returns {Object} 返回GeoJson的FeatureCollection对象,包括点、线、面、多点、多线、多面类型
* @example
* // 获取用户数据,将数据输出至会话窗口
* let data = this.getUserData();
* if (data) this.chatLog(JSON.stringify(data, null, 4));
*/
getUserData() {
return this.context.userData;
}
/**
* 切换CAD图层
* @example
* // 切换CAD图层
* this.switchCadLayers();
*/
async switchCadLayers() {
// 先获取默认的图层
let layers = this.map.getService().getMapLayers();
let layerNames = layers.map(layer => layer.name);
let openLayers = layers.filter(layer => layer.isOff === false).map(layer => layer.name);
const getSelectLayers = (selectNames) => {
let selectLayers = [];
for (let layerName of layerNames) {
let isOpen = selectNames.includes(layerName);
if (isOpen) {
selectLayers.push({name: layerName, isOff: false});
} else {
selectLayers.push({name: layerName, isOff: true});
}
}
return selectLayers;
}
let isClickApply = false;
let selectLayerNames = await this.selectOptionsValue(layerNames, {
isMultiSelect: true,
title: "选择图层",
initValue: openLayers,
applyCallback: async (selectNames) => {
isClickApply = true;
// 点击应用时,也切换图层
await vjcommon.switchCadLayers(this.map, getSelectLayers(selectNames), this.map.hasVectorLayer());
}
});
if (!selectLayerNames) {
if (isClickApply) {
// 如果点击了应用,取消时,切换到原来的图层
await vjcommon.switchCadLayers(this.map, getSelectLayers(openLayers), this.map.hasVectorLayer());
}
return;
}
// 切换图层
await vjcommon.switchCadLayers(this.map, getSelectLayers(selectLayerNames), this.map.hasVectorLayer());
// 添加撤销记录
let oldLayers = getSelectLayers(openLayers);
let isVector = this.map.hasVectorLayer();
this.context.ui.addUndoRecord(() => {
vjcommon.switchCadLayers(this.map, oldLayers, isVector);
});
}
/**
* 增加一个点标记Marker
* @param {[number, number]} pt 要设置的坐标[x, y]
* @param {Record<string, any>} options 其他参数,参数可选
* @param {string} options.color 颜色,参数可选,默认为#3FB1CE
* @param {any} options.popupContent 弹窗内容,参数可选,默认为空表示不显示弹窗
* @param {[number, number]} options.popupOffset 弹窗偏移量,参数可选,默认为[0, -40]
* @param {boolean} options.popupShowInForm 弹窗内容是否以表单形式显示,参数可 选,默认为false
* @param {boolean} options.popupOpen 弹窗内容是否打开,参数可选,默认为false
* @param {number} options.popupWidth 弹窗宽度,参数可选
* @param {number} options.popupHeight 弹窗高度,参数可选
* @param {string} options.anchor 锚点, 选项包括'center','top','bottom','left','right','top-left','top-right','bottom-left',和'bottom-right'。参数可选,默认为'center'
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {number} options.rotation 旋转角度,参数可选,默认为0
* @param {string} options.icon 自定义图标名称,参数可选,默认为空
* @param {number} options.elevation 坐标标高值,参数可选,默认为0
* @param {any} options.data 数据内容,参数可选,默认为undefined
* @param {boolean} options.visible 是否显示,参数可选,默认为true
* @returns {string} 返回id
* @example
* // 示例一
* // 在坐标[10,20]处添加一个红色的点标记
* let markerId = this.addMarker([10, 20], {
* color: "#FF0000"
* });
* console.log(markerId);
* // 示例二
* // 获取用户数据中的所有点类型的数据,在相应位置添加点标记, 弹窗内容为数据的属性值
* let data = this.getUserData();
* let features = data.features.filter(f => f.geometry.type == "Point");
* features.forEach(f => {
* this.addMarker(f.geometry.coordinates, {
* popupContent: f.properties
* });
* })
* // 示例三
* // 在地图范围内随机生成一个点,在这个位置创建一个marker,图标为sensor,可拖动。
* let points = this.randomPointInMapBounds(1);
* let x = points[0][0];
* let y = points[0][1];
* this.addMarker([x, y], {
* draggable: true,
* icon: "sensor"
* });
*/
addMarker(pt, options) {
options = options || {};
let element;
if (options.icon) {
// 获取自定义图标数据, 在工具栏中的"地图图像管理"中对自定义图标进行管理
let image = this.context.ui.getCustomImages(options.icon);
if (image) {
let el = document.createElement('div');
el.className = 'marker';
let imgValue = image.value;
if (imgValue.toLowerCase().indexOf("<svg") == 0) {
//svg或//base64 png
imgValue = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(imgValue))) ;
}
el.style.backgroundImage =
`url("${imgValue}")`;
el.style.width = (image.width ?? 40) + 'px';
el.style.height = (image.height ?? 40) + 'px';
el.style.backgroundSize = '100%';
element = el;
}
}
let marker = new vjmap.Marker({
color: options.color,
anchor: options.anchor,
draggable: options.draggable,
rotation: options.rotation,
element: element
})
marker.setLngLat(this.map.toLngLat(pt));
marker.addTo(this.map);
marker.id = "marker_" + vjmap.RandomID()
if (options.data) marker.data = options.data;
if (options.elevation) marker.setHeight(options.elevation);
if (options.popupContent) {
let popup = new vjmap.Popup({
offset: options.popupOffset || [0, -40]
})
const dom = this.context.ui.createElementByJsonData(options.popupContent, options.popupShowInForm, {
width: options.popupWidth,
height: options.popupHeight
})
popup.setDOMContent(dom.element)
marker.setPopup(popup)
if (options.popupOpen) {
marker.togglePopup()
}
}
if (options.visible !== undefined) {
marker.setVisible(options.visible, true);
}
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteMarker(marker.id);
});
return marker.id
}
/**
* 根据根据的坐标位直接绘制一个点符号
* @param {[number, number]} point 点符号的坐标[x, y],参数必填
* @param {object} options 配置参数, 参数可选
* @param {string} options.icon 符号图标名称,默认为_markerIcon_,参数可选
* @param {number} options.iconSize 符号图标放大倍数,默认为1,参数可选
* @param {number} options.iconRotate 符号图标旋转角度,默认为0,参数可选
* @param {string} options.text 符号文本内容,默认为空,参数可选
* @param {string} options.textColor 符号文本颜色,默认为空,参数可选
* @param {number} options.textSize 符号文本大小,默认为22,参数可选
* @param {number} options.textRotate 符号文本旋转角度,默认为0,参数可选
* @param {number} options.textOpacity 符号文本透明度,默认为0,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 示例一
* // 在位置[10, 20]处绘制一个点符号
* const pt = this.createSymbol([10, 20]); // 启用捕捉功能采绘制一个点符号
* if (!pt) return;
* // 点的坐标
* let point = pt.point;
* const x = point[0];
* const y = point[1];
* console.log(x, y);
* // 图元ID
* let id = pt.id;
* console.log(id);
* // 示例二
* // 在地图范围内随机生成1个点, 在这个点位置绘制一个点符号,符号图标名称为_pulsingDot_
* let points = this.randomPointInMapBounds(1);
* const pt2 = this.createSymbol(points[0], {
* icon: "_pulsingDot_"
* });
* console.log(pt2);
* // 示例三
* // 在地图范围内随机生成10个点, 根据这些位置分别绘制点符号,符号图标名称为空,文本内容为当前坐标值, 颜色为红色
* let points2 = this.randomPointInMapBounds(10);
* for (let i = 0; i < points2.length; i++) {
* let coord = points2[i];
* this.createSymbol(coord, {
* icon: "",
* text: `[${coord[0].toFixed(2)}, ${coord[1].toFixed(2)}]`,
* textColor: "#FF0000"
* });
* }
*/
createSymbol(point, options) {
options = options || {};
if (!point) return;
let draw = this.map.getDrawLayer();
let id = vjmap.RandomID(10)
let icon = options.icon;
if (icon === undefined) icon = "_markerIcon_";
let onlyText = options.text && !icon;
let symbolAttr = icon ? {
"icon_image": icon,
"icon_size": options.iconSize || 1,
"icon_rotate": options.iconRotate || 0,
} : undefined;
let textAttr = options.text ? {
"text": options.text,
"text_size": options.textSize,
"text_rotate": options.textRotate,
"text_opacity": options.textOpacity,
"color": options.textColor
} : undefined
draw.add({
id,
type: 'Feature',
geometry: {
type: 'Point',
coordinates: this.map.toLngLat(point)
},
properties: {
"symbol": true,
"onlyText": onlyText,
"data": options.data,
...symbolAttr,
...textAttr,
}
});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.getDrawLayer().delete(id);
});
return {
id,
point
};
}
/**
* 弹出表单输入对话框
* @param {object} formJsonSchema 表单的 JSON Schema 格式配置,包含表单字段、类型、校验规则和属性的定义。结构示例:
* @returns {object} 返回用户输入的数据,格式为一个对象,其中每个字段的键为 `properties` 中的字段名,值为用户输入的值。例如:
* @example
* // 弹出学生表单输入对话框,包括姓名、年龄(默认18)、性别
* let formJsonSchema = {
* type: "object",
* title: "学生信息表单",
* properties: {
* name: { type: "string", title: "姓名"},
* age: { type: "number", title: "年龄", default: 18},
* gender: { type: "string", title: "性别", enum: ["男", "女"]}
* },
* required: ["name", "age", "gender"]
* }
* let res = await this.showFormInputDialog(formJsonSchema);
* let name = res.name;
* let age = res.age;
* let gender = res.gender;
* console.log(name, age, gender)
*/
async showFormInputDialog(formJsonSchema) {
return await this.context.ui.showFormInputDlg(formJsonSchema)
}
/**
* 创建荧光点数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {string} options.text 要显示的文字,参数可选,默认为"index ${props.index}"
* @param {number} options.minZoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxZoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isDrawLinePoint 绘制点时包括线上所有点, 参数可选,默认为false
* @param {number} options.width 像素宽度,参数可选,默认为24
* @param {number} options.markerHeight 像素高度,参数可选
* @param {string} options.color1 颜色值一,参数可选
* @param {string} options.color2 颜色值二,参数可选
* @param {number} options.textFontSize 字体大小,参数可选
* @param {string} options.textColor 字体颜色,参数可选
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {string} options.animationType 动画类型,参数可选,无动画 MAP_ANIMATION_NONE,弹跳 MAP_ANIMATION_BOUNCE, 坠落 MAP_ANIMATION_DROP
* @param {number} options.rotation 旋转角度,参数可选
* @param {string} options.rotationAlignment 旋转对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {string} options.pitchAlignment 倾斜对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {number} options.height 坐标标高值,参数可选
* @param {number} options.scaleMaxZoom 能缩放的最大级别,参数可选
* @param {boolean} options.removeWhenNoInMapView 视图范围内自动显隐,参数可选
* @param {number} options.removeWhenNoInMapViewPadding 视图范围内自动显隐Padding像素范围,参数可选,默认500
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer14的荧光点数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createFluorescenceMarkerLayer(geojson, {
* layerName: 'layer14'
* });
* console.log(layer);
*/
async createFluorescenceMarkerLayer(data, options) {
options = options || {};
let type = "fluorescenceMarker";
if (options.height) {
options.height = this.map.geoLengthToHeight(options.height);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
text: "ID:${props.index}",
width: 20,
markerHeight: 20,
color1: "#ff0000",
color2: "#ffff00",
textFontSize: 15,
textColor: "#00ffff",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createFluorescenceMarkerLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 创建发光的光环数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {string} options.text 要显示的文字,参数可选,默认为"index ${props.index}"
* @param {number} options.minZoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxZoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isDrawLinePoint 绘制点时包括线上所有点, 参数可选,默认为false
* @param {number} options.width 像素宽度,参数可选,默认为24
* @param {number} options.markerHeight 像素高度,参数可选
* @param {string} options.color1 颜色值一,参数可选
* @param {string} options.color2 颜色值二,参数可选
* @param {number} options.textFontSize 字体大小,参数可选
* @param {string} options.textColor 字体颜色,参数可选
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {string} options.animationType 动画类型,参数可选,无动画 MAP_ANIMATION_NONE,弹跳 MAP_ANIMATION_BOUNCE, 坠落 MAP_ANIMATION_DROP
* @param {number} options.rotation 旋转角度,参数可选
* @param {string} options.rotationAlignment 旋转对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {string} options.pitchAlignment 倾斜对齐,参数可选, 自动 auto, 相对于地图 map, 相对于视口 viewport
* @param {number} options.height 坐标标高值,参数可选
* @param {number} options.scaleMaxZoom 能缩放的最大级别,参数可选
* @param {boolean} options.removeWhenNoInMapView 视图范围内自动显隐,参数可选
* @param {number} options.removeWhenNoInMapViewPadding 视图范围内自动显隐Padding像素范围,参数可选,默认500
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer15的发光的光环数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createHaloRingMarkerLayer(geojson, {
* layerName: 'layer15'
* });
* console.log(layer);
*/
async createHaloRingMarkerLayer(data, options) {
options = options || {};
let type = "haloRingMarker";
if (options.height) {
options.height = this.map.geoLengthToHeight(options.height);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
text: "ID:${props.index}",
width: 40,
markerHeight: 40,
color1: "#ff0000",
color2: "#ffff00",
textFontSize: 15,
textColor: "#00ffff",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createHaloRingMarkerLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 获取所有数据图层
* @returns {Array<Object>} 所有数据图层, 返回的数据图层对象包含
* @example
* // 获取所有数据图层的图层名称
* let layers = this.getAllDataLayers();
* let layerNames = layers.map(layer => layer.layerName);
* console.log("所有数据图层名称:", layerNames);
*/
getAllDataLayers() {
let layers = this.context.global.layers;
if(layers) {
return layers.map(item => {
let layerId = item.layerId;
let sourceId = item.sourceId;
if (item.layer && typeof(item.layer.getLayerId) == "function") {
layerId = item.layer.getLayerId();
let style = this.map.getStyle();
let layer = style.layers.find(layer => layer.id == layerId);
if (layer && layer.source) {
sourceId = layer.source;
}
}
return {
layerId: layerId,
sourceId: sourceId,
visible: item.visible,
type: item.type,
layer: item.layer,
name: item.name
}
});
} else {
return [];
}
}
/**
* 导出为PDF文件
* @param {Object} options 导出PDF的选项,参数可选
* @param {string} options.mapid 地图id,默认当前地图
* @param {string} options.version 地图版本,默认当前地图版本
* @param {boolean} options.exportDrawLayer 是否导出绘图图层,默认false
* @param {boolean} options.bIncludeOffLayers 是否包含关闭的图层,默认false
* @param {boolean} options.bSearchableSHX 是否包含可搜索型文字,默认false
* @param {boolean} options.bSearchableTTF 是否包含可搜索ttf文字,默认false
* @param {number} options.pageWidth 页面宽度,单位mm,默认210 (A4)
* @param {number} options.pageHeight 页面高度,单位mm,默认297 (A4)
* @param {number} options.pageLeftMargin 左页边距,单位mm,默认0
* @param {number} options.pageRightMargin 右页边距,单位mm,默认0
* @param {number} options.pageTopMargin 上页边距,单位mm,默认0
* @param {number} options.pageBottomMargin 下页边距,单位mm,默认0
* @param {number} options.geomDPI 矢量图形DPI,默认600
* @param {number} options.colorImagesDPI 图像DPI,默认400
* @param {boolean} options.isBlackWhiteMode 是否导出为黑白模式,默认false
* @param {boolean} options.isGrayMode 是否导出为灰色模式,默认false
* @param {boolean} options.disableOpen 只返回地址,不打开图片,默认false
* @example
* // 示例一
* // 导出为PDF文件
* await this.exportPdf();
* // 示例二
* // 导出为PDF文件, 并包含绘图图层
* await this.exportPdf({exportDrawLayer: true});
* // 示例三
* // 导出为PDF文件, mapid为sys_world, 灰度模式
* await this.exportPdf({mapid: "sys_world", isGrayMode: true});
*/
async exportPdf(options) {
this.showMessage("正在导出Pdf并打开,请稍候...", "info" );
options = options || {};
let mapid = options.mapid || this.map.getService().currentMapParam()?.mapid;
let version = options.version || this.map.getService().currentMapParam()?.version;
if (!options.mapid &&options.exportDrawLayer === true) {
const res = await vjcommon.exportDwg(this.map, this.map.getDrawLayer());
if (res.error) {
this.showMessage(res.error, "error" );
return;
}
mapid = res.mapid;
version = res.version;
}
let param = options; // 不填的话,用默认的
/*
param = {
bIncludeOffLayers: false, // 是否包含关闭的图层
bSearchableSHX: false, // 可搜索型文字
bSearchableTTF: false, // 可搜索ttf文字
pageWidth: 210, // 宽,单位mm
pageHeight: 297, // 高,单位mm
pageLeftMargin: 0, // 左页边距 (pageWidth, pageHeight有值时有效)
pageRightMargin: 0, // 右页边距 (pageWidth, pageHeight有值时有效)
pageTopMargin: 0, // 上页边距 (pageWidth, pageHeight有值时有效)
pageBottomMargin: 0, // 下页边距 (pageWidth, pageHeight有值时有效)
geomDPI: 600, // 矢量dpi
colorImagesDPI: 400, //图像dpi
isBlackWhiteMode: false, // 是否导出为黑白模式
isGrayMode: false, // 是否导出为灰色模式
}*/
let svc = this.map.getService();
const result = await svc.execCommand("exportPdf", param, mapid, version, true);
if (result.error) {
this.map.logInfo(result.error, "error");
} else {
let pdfUrl = svc.baseUrl() + result.path + "?token=" + svc.accessToken;
if (!options.disableOpen) {
window.open(pdfUrl )
}
return pdfUrl;
}
}
/**
* 弹出数据创建修改对话框
* @param {object} options 数据面板配置,可选
* @param {any} options.data 要修改的数据内容,可选,为空时表示是创建数据
* @param {string} options.title 数据面板标题,可选
* @param {number} options.width 数据面板宽度,可选
* @param {number} options.height 数据面板高度,可选
* @param {boolean} options.showForm 是否以表单形式显示,可选,默认false
* @param {Function} options.applyCallback 点击应用按钮的回调函数,可选
* @returns {any} 返回用户输入的数据, 如果用户点击取消,则返回undefined
* @example
* // 弹出数据创建对话框,在控制台输出数据
* let res = await this.showDataModifyDlg();
* if (res) {
* console.log(res)
* }
* // 获取图层名称为layer1的数据图层属性,弹出数据修改对话框对其进行修改,数据为图层属性,数据面板以表单形式显示,点击应用按钮回调以及修改结束后,设置图层数据图层属性为修改后的值。
* let layerName = 'layer1';
* let property = this.getDataLayerProperty(layerName);
* let value = await this.showDataModifyDlg({
* data: property,
* title: "图层属性",
* showForm: true,
* applyCallback: (val) => {
* this.setDataLayerProperty(layerName, val);
* }
* });
* if (value) {
* this.setDataLayerProperty(layerName, value);
* }
*/
async showDataModifyDlg(options) {
options = options || {};
if (!options.data) {
// 如果是创建数据,则以json输入框显示
options.showForm = false;
}
return await this.context.ui.showDataModifyDlg(options.data, options.showForm, options);
}
/**
* 采集一个圆的坐标
* @param {boolean} isSnap 是否启用捕捉功能, 默认false
* @returns {Promise<Object | undefined>} 返回圆心坐标和半径,如果取消操作则返回undefined, 返回的对象包含以下属性:
* @example
* const circle = await this.actionDrawCircle();
* if (!circle) return;
* // 圆心坐标
* const centerX = circle.center[0];
* const centerY = circle.center[1];
* // 半径
* const radius = circle.radius;
* console.log(centerX, centerY, radius);
*/
async actionDrawCircle(isSnap) {
this.context.tools.logInfo("请在地图上绘制一个圆")
let snapObj = {};
if (isSnap) {
vjcommon.getMapSnapPoints(this.map, snapObj);
}
let drawCircle = await vjmap.Draw.actionDrawCircle(this.map, isSnap ? {
api: {
getSnapFeatures: snapObj
}
} : {});
if (drawCircle.cancel) {
return;
}
let center = this.map.fromLngLat(drawCircle.features[0].properties.center);
let circlePoint = this.map.fromLngLat(drawCircle.features[0].geometry.coordinates[0][0]);
let radius = center.distanceTo(circlePoint);
return {
center: [center.x, center.y],
radius
};
}
/**
* 处理图形组合生成新地图
* @param {object | object[]} options 参数 可选。可以是一个对象或对象数组。如果是对象,属性值是以下参数说明中的。如果是数组,每项的内容和对象的内容是一样的。
* @param {string} [options.mapid] 地图ID. 可选,为空时则获取当前打开的mapid
* @param {string} [options.version] 地图版本(为空时采用当前打开的地图版本). 可选
* @param {[number, number, number, number]} [options.clipbounds] 地图裁剪范围,范围如[x1,y1,x2,y2],为空的话,表示不裁剪. 可选
* @param {boolean} [options.selByCrossing] 选择是包含还是相交(默认false表示包含,true相交). 可选
* @param {[number, number, number, number]} [options.fourParameter] 四参数(x偏移,y偏移,缩放,旋转弧度),可选,对坐标最后进行修正. 可选
* @param {boolean} [options.isInverseFourParamter] 是否用上面的四参数进行反算,默认false. 可选
* @param {string[]} [options.layers] 要显示的图层名称,为空的时候,表示全部图层. 可选
* @param {boolean} [options.purge] 生成后清理图形数据以减少dwg文件大小. 可选
* @param {boolean} [options.isZoomExtents] 是不是创建完缩放至全图 默认false (如果有多个图合并,只需设置第一个图的). 可选
* @param {string | "*" | "cad2000" | "cad2004" | "cad2007" | "cad2010" | "cad2013" | "cad2018"} [options.cadVersion] 导出的CAD版本号。如果为*表示为当前CAD图的版本. 可选
* @returns {Promise<string|undefined>} 返回生成的fileid, 如果失败则返回undefined
* @example
* //示例一
* // 处理图形组合生成新地图, 要显示的图层列表为["0"]
* let res = await this.composeNewMap({
* layers: ["0"]
* });
* if (res) {
* this.showMessage("生成新地图成功,fileid为" + res, "success");
* }
* //示例二
* // 把mapid为sys_hello和sys_world的地图处理图形组合生成新地图
* let res2 = await this.composeNewMap([{
* mapid: "sys_hello"
* }, {
* mapid: "sys_world"
* }]);
* if (res2) {
* this.showMessage("生成新地图成功,fileid为" + res2, "success");
* }
*/
async composeNewMap(options) {
let svc = this.map.getService();
options = options || {};
let currentMap = svc.currentMapParam() || {};
if (Array.isArray(options)) {
for(let opt of options) {
if (!opt.mapid) {
opt.mapid = currentMap.mapid;
}
}
} else {
if (!options.mapid) {
options.mapid = currentMap.mapid;
}
}
let res = await svc.composeNewMap(options);
if (res && res.error) {
this.showMessage(res.error)
return;
}
return res.fileid;
}
/**
* 获取临时数据
* @param {string} name 数据名
* @returns {any} 数据内容
* @example
* // 获取临时数据test的值
* this.getTempData("test");
*/
getTempData(name) {
return this.context.resultData[name]
}
/**
* 创建动画线数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {string} options.lineColor 线条颜色
* @param {number} options.lineWidth 线条宽度
* @param {number} options.lineOpacity 线条透明度,取值范围0-1
* @param {number} options.lineGapWidth 线条间隙宽度,在线的实际路径之外绘制描边时的内部间隙宽度,单位像素,默认值1
* @param {number} options.lineOffset 线的偏移量。线性要素正值向右偏移,负值向左偏移;多边形正值向内偏移,负值向外偏移,单位像素
* @param {number} options.lineBlur 线条模糊度,配合宽度使用,该值应小于线宽度
* @param {[number,number]} options.lineDasharray 虚线样式,数组格式如[1,1]表示实线、虚线的组合
* @param {[number,number]} options.lineTranslate 几何体的平移量[x,y],负数分别表示左和上,单位像素,默认[0,0]
* @param {string} options.lineCap 线帽形状,可选值:"butt"(尖头)、"round"(圆头)、"square"(平头)
* @param {string} options.lineJoin 拐角形状,可选值:"bevel"(平拐)、"round"(圆拐)、"miter"(棱拐)
* @param {string} options.linePattern 线的拉伸图片类型,必须与地图已加载的图片名称对应,图像宽度必须是2的系数(2、4、8、...、512)
* @param {string} options.lineGradient 线条渐变
* @param {string} options.animateImagesType 动画类型,可选值:"arrow"(箭头动画), "antpath"(蚂蚁线动画), "customImage"(自定义图片), "imageSprites"(同一个图片精灵动画集)
* @param {string} options.animateImages_arrowFillColor 箭头填充颜色,当animateImagesType为"arrow"时有效,默认"#22B14C"
* @param {string} options.animateImages_arrowStrokeColor 箭头边框颜色,当animateImagesType为"arrow"时有效,默认"#fff"
* @param {number} options.animateImages_arrowStrokeWidth 箭头线宽,当animateImagesType为"arrow"时有效
* @param {number} options.animateImages_arrowWidth 箭头大小,当animateImagesType为"arrow"时有效
* @param {string} options.animateImages_fillColor1 蚂蚁线填充颜色1,当animateImagesType为"antpath"时有效
* @param {string} options.animateImages_fillColor2 蚂蚁线填充颜色2,当animateImagesType为"antpath"时有效
* @param {string} options.customImageUrl 自定义图片URL或base64,当animateImagesType为"customImage"时有效
* @param {string} options.imageSpriteUrl 图片动画集URL或base64,当animateImagesType为"imageSprites"时有效
* @param {number} options.animateImages_spriteWidth 来源图片中精灵图所占宽度,当animateImagesType为"imageSprites"时有效
* @param {number} options.animateImages_spriteHeight 来源图片中精灵图所占高度,当animateImagesType为"imageSprites"时有效
* @param {number} options.animateImages_canvasWidth 画布宽度,所有动画类型通用
* @param {number} options.animateImages_canvasHeight 画布高度,所有动画类型通用
* @param {number} options.animateImages_frameCount 动画帧数,所有动画类型通用
* @param {boolean} options.animateImages_directionReverse 动画方向是否反向,所有动画类型通用
* @param {number} options.speed 动画速度,步长0.1
* @param {boolean} options.startAutoAnimation 是否开始时自动播放动画
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer3的动画线数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createAnimateLineLayer(geojson, {
* layerName: 'layer3'
* });
* console.log(layer);
*/
async createAnimateLineLayer(data, options) {
options = options || {};
let type = "animateLine";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
lineColor: '#ff0000',
lineWidth: 10,
animateImagesType: "arrow",
animateImages_arrowFillColor: "#22B14C",
animateImages_arrowStrokeColor: "#fff",
animateImages_arrowStrokeWidth: 6,
animateImages_canvasWidth: 128,
animateImages_canvasHeight: 32,
animateImages_arrowWidth: 16,
animateImages_frameCount: 4,
animateImages_fillColor1: "#f0fb",
animateImages_fillColor2: "#0ffb",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createAnimateLineLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 创建多边形拉伸数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {string} options.fillExtrusionColor 拉伸颜色,参数可选,默认为"#00ffff"
* @param {number} options.fillExtrusionOpacity 透明度,参数可选,取值范围0-1
* @param {number} options.fillExtrusionHeight 拉伸高度,单位CAD图制图单位,参数可选,默认为0
* @param {number} options.fillExtrusionBase 拉伸底部高度,单位CAD图制图单位,参数可选,默认为0
* @param {string} options.fillExtrusionPattern 拉伸填充图案,参数可选,使用雪碧图中的图像名称,图像宽度必须是2的系数
* @param {[number,number]} options.fillExtrusionTranslate 平移量,参数可选,格式[x,y],负数表示左和上,单位像素,默认[0,0]
* @param {string} options.fillExtrusionTranslateAnchor 平移参考系,参数可选,"map"表示相对地图平移,"viewport"表示相对视窗平移
* @param {boolean} options.fillExtrusionVerticalGradient 是否应用垂直渐变,参数可选,true时侧面下方位置会略暗
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer12的多边形拉伸数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createFillExtrusionLayer(geojson, {
* layerName: 'layer12'
* });
* console.log(layer);
*/
async createFillExtrusionLayer(data, options) {
options = options || {};
let type = "fillExtrusion";
if(options.fillExtrusionHeight) {
options.fillExtrusionHeight = this.map.geoLengthToHeight(options.fillExtrusionHeight);
}
if(options.fillExtrusionBase) {
options.fillExtrusionBase = this.map.geoLengthToHeight(options.fillExtrusionBase);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
fillExtrusionColor: "#00ffff",
fillExtrusionHeight: this.map.geoLengthToHeight(this.map.getGeoBounds().width() / 6),
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createFillExtrusionLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 输出内容到会话窗口
* @param {string} content 输出内容, 支持markdown格式
* @example
* // 将"123"输出至会话窗口
* this.chatLog("123");
*/
chatLog(content) {
return this.context.ui.chatLog(content)
}
/**
* 创建绘制数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.entColorToHtmlColor 颜色转化为html颜色,参数可选
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer10的绘制数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createDrawDataLayer(geojson, {
* layerName: 'layer10',
* });
* console.log(layer);
*/
async createDrawDataLayer(data, options) {
options = options || {};
let type = "draw";
if (options.entColorToHtmlColor === undefined) {
for(let f of data.features) {
if (f.properties && typeof f.properties.color == "number") {
if (f.properties.color < 0) {
options.entColorToHtmlColor = true;
break;
}
}
}
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createDrawDataLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 导出图纸布局空间
* @param {object} options 参数
* @param {string} options.mapid 地图ID. 可选,为空时则使用当前打开的地图
* @param {string} [options.version] 地图版本(为空时采用当前打开的地图版本)
* @param {string} [options.layoutIndex] 布局索引,layoutIndex和layoutName只要输入一个就行,layoutIndex优先。如果都为空,则使用dwg当前的布局空间
* @param {string} [options.layoutName] 布局名称,layoutIndex和layoutName只要输入一个就行,layoutIndex优先。如果都为空,则使用dwg当前的布局空间
* @returns {Promise<string>} 返回导出的fileid, 如果失败则返回空字符串
* @example
* // 导出当前图纸布局空间,布局索引为0
* let fileid = await this.exportLayout({
* layoutIndex: "0"
* });
* console.log(fileid);
*/
async exportLayout(options) {
let svc = this.map.getService();
options = options || {};
options.mapid = options.mapid || svc.currentMapParam().mapid;
let res = await svc.cmdExportLayout(options);
if (res && res.fileid) {
return res.fileid;
}
return "";
}
/**
* 增加一个功能菜单及实现功能
* @param {string} name 菜单名
* @param {Function} func 要实现的功能函数
* @example
* // 增加一个功能菜单,菜单名为全图显示,功能为将地图缩放至全图显示
* this.addMenuItem("全图显示", () => {
* //缩放至全图显示
* this.fitMapBounds();
* })
*/
addMenuItem(name, func) {
// 默认情况下增加的功能菜单,地图右键菜单中进行显示
this.context.ui.addMenuItem(name, func);
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.removeMenuItem(name);
});
}
/**
* 查询图中数据
* @param {string} sql sql语句,数据库表名称为geom, 表结构如下
* @returns {Object} 返回标准的GeoJson格式的FeatureCollection对象, 包含features属性, 每个feature包含geometry和properties属性,properties属性中包含如下字段:
* @example
* // 示例一
* // 查询图中类型为 line 且图层索引为 2 的记录
* let result1 = await this.queryMapData("SELECT * FROM geom WHERE name = 'AcDbLine' AND layerIndex = 2");
* if (result1.features.length > 0) {
* let feature = result1.features[0];
* // 获取线宽
* console.log(feature.properties.lineWidth)
* // 获取所有点的坐标,在坐标处绘制一个Marker
* const addMarker = (co) => {
* // co的格式为[x, y]
* this.addMarker(co)
* }
* const processGeoJSONFeature = (geometry) => {
* if (geometry.type === "Point") {
* let co = geometry.coordinates;
* addMarker(co)
* } else if (geometry.type === "MultiPoint") {
* geometry.coordinates.forEach(co => addMarker(co))
* } else if (geometry.type === "LineString") {
* geometry.coordinates.forEach(co => addMarker(co))
* } else if (geometry.type === "MultiLineString") {
* geometry.coordinates.forEach(line => {
* line.forEach(co => addMarker(co))
* })
* } else if (geometry.type === "Polygon") {
* geometry.coordinates.forEach(line => {
* line.forEach(co => addMarker(co))
* })
* } else if (geometry.type === "MultiPolygon") {
* geometry.coordinates.forEach(polygon => {
* polygon.forEach(line => {
* line.forEach(co => addMarker(co))
* })
* })
* }
* }
* if (feature.geometry.type === "GeometryCollection") {
* feature.geometry.geometries.forEach(ft => {
* processGeoJSONFeature(ft)
* })
* } else {
* processGeoJSONFeature(feature.geometry)
* }
* }
* // 示例二
* // 查询图中全部文本,坐标类型为几何坐标
* let result2 = await this.queryMapData("SELECT * FROM geom WHERE (name = 'AcDbText' or name = 'AcDbMText' or name = 'AcDbAttributeDefinition' or name = 'AcDbAttribute') and coordType = true");
* if (result2.features.length > 0) {
* result2.features.forEach(feature => {
* // 获取文本内容
* console.log(feature.properties.content)
* });
* }
* // 查询图中包含矩形范围为[10, 10, 20, 20]的全部线,坐标类型为位置坐标
* let result3 = await this.queryMapData("SELECT * FROM geom WHERE (name = 'AcDbLine' or name = 'AcDbPolyline' or name = 'AcDb2dPolyline' or name = 'AcDb3dPolyline') and bounds ='[10, 10, 20, 20]' and isContains = true and coordType = false");
* // 获取记录总数
* console.log(result3.features.length)
*/
queryMapData(sql) {
return this.context.tools.queryMapData(this.context, sql)
}
/**
* 自动提取子图范围
* @returns {Promise<Object>} 如果失败则返回undefined, 否则返回提取出的子图范围的GeoJSON数据和子图范围的bounds数组, 返回格式如下:
* @example
* // 自动提取子图范围
* let res = await this.findChildMapRects();
* console.log(res);
*/
async findChildMapRects(options) {
options = options || {};
let svc = this.map.getService();
options = {
mapid: options.mapid || svc.currentMapParam().mapid,
bounds: "",
condition: "",
debugInfo: [],
digit: 4,
tol: 0,
tableEdgeMinPoint: 12,
tableTextMinCount: 4,
cellMaxArea: 90,
cellEmptyRatio: 90,
unLinkLineRatio: 20,
tableMaxCellCount: 100000,
noHvLineSegCount: 0,
seachTableMost: false,
version: svc.currentMapParam()?.version,
layer: svc.currentMapParam()?.layer,
// 如果直接内存打开的话,用表达式查询,如果几何渲染打开,用空间查询
geom: svc.currentMapParam()?.mapopenway != vjmap.MapOpenWay.Memory,
...options,
};
if (options.isSelectBounds) {
// 如果是手动,先在图中选择一个区域
if (this.map.logInfo) this.map.logInfo("请先选择表格所在的矩形区域,选择区域的范围需要覆盖表格内容", "success", 10000);
// 先在地图上绘制一个矩形,做为裁剪区域
let snapObj = {}; // 设置的捕捉的实体
vjcommon.getMapSnapPoints(this.map, snapObj);
let res = await vjmap.Draw.actionDrawRectangle(this.map, {
api: {
getSnapFeatures: snapObj //要捕捉的数据项在后面,通过属性features赋值
},
contextMenu: (e) => {
// 给地图发送Enter键消息即可取消,模拟按Enter键
this.map.fire("keyup", {keyCode:13})
}
});
if (res.cancel) {
return // 取消操作
}
// 获取所绘制的坐标
let rectCoord = this.map.fromLngLat(res.features[0].geometry.coordinates[0]);
// 取左下角和右下角两个点
let bounds = [rectCoord[0].x, rectCoord[0].y, rectCoord[2].x, rectCoord[2].y];
options.bounds = bounds.join(',');
}
if (Array.isArray(options.bounds)) {
options.bounds = JSON.stringify(options.bounds);
}
if (options.showSettingForm === true) {
options = await this.showDataModifyDlg({
data: options,
title: "自动提取表格设置",
showForm: true
})
if (!options) return;
}
// 只获取子图范围
options.findChildMapRects = true;
let result = await svc.cmdExtractTale(options);
if (result && result.error) {
if (this.map.logInfo) this.map.logInfo(result.error, "error", 10000);
return;
}
if (!result.childMapRects) return;
let geoDatas = {
"type": "FeatureCollection",
"features": []
}
let childMapBounds = [];
for (let i = 0; i < result.childMapRects.length; i++) {
let bounds = vjmap.GeoBounds.fromArray(result.childMapRects[i].bounds);
childMapBounds.push(bounds.toString())
let points = bounds.toPointArray();
points.push(points[0]); // 闭合
geoDatas.features.push({
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [points]
},
properties: {
color: vjmap.randomColor(),
index: i,
data: result.childMapRects[i]
}
});
}
return {
geojson: geoDatas,
childMapBounds: childMapBounds
}
}
/**
* 根据给定的圆心和半径直接绘制一个圆
* @param {[number, number]} center 圆心坐标[x,y],参数必填
* @param {number} radius 半径,参数必填
* @param {object} options 配置参数, 参数可选
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {boolean} options.isFill 是否填充,默认false,参数可选
* @param {number} options.opacity 透明度,默认1,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Object} 返回绘制的图元对象,返回的对象包含以下属性:
* @example
* // 创建一个地图范围内随机位置的圆, 圆的半径为地图宽度的1/40, 颜色随机,填充,透明度0.5
* let points = this.randomPointInMapBounds(1);
* let rect = this.getMapGeoBounds(); // 获取地图范围
* let width = (rect[2] - rect[0]) / 40; // 矩形宽度为地图宽度的1/00
* const circle = this.createCircle(points[0], width, {
* color: vjmap.randomColor(),
* isFill: true,
* opacity: 0.5
* });
* console.log(`圆 ID:`, circle.id);
* console.log(`圆 圆心:`, circle.center);
* console.log(`圆 半径:`, circle.radius);
*/
createCircle(center, radius, options) {
let draw = this.map.getDrawLayer();
let coords = vjmap.getCirclePolygonCoordinates(center, radius);
coords = coords.map(c => this.map.toLngLat(c));
let id = vjmap.RandomID(10);
draw.add({
id,
type: 'Feature',
geometry: {
type: options.isFill ? 'Polygon' : 'LineString',
coordinates: options.isFill ? [coords] : coords
},
properties: {
color: options.color,
opacity: options.opacity,
data: options.data
}
});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.getDrawLayer().delete(id);
});
return {
id,
center,
radius
};
}
/**
* 将地图经纬度坐标转换为CAD图几何坐标
* @returns {[number, number]} 返回包含两个数字的数组[x,y],表示CAD图几何坐标
* @example
* // 将地图经纬度坐标[10, 20]转换为CAD图几何坐标
* let point = this.lngLatToGeoPoint([10, 20]);
* let x = point[0];
* let y = point[1];
* console.log(x, y);
*/
lngLatToGeoPoint(lngLat) {
let geoPoint = this.map.fromLngLat(lngLat);
return [geoPoint.x, geoPoint.y];
}
/**
* 根据范围进行闪烁
* @param {[number,number,number,number]} bounds 范围
* @param {Object} options 配置参数, 参数可选
* @param {string} options.color 颜色,格式为#RRGGBB,默认为#FFFF00参数可选
* @param {number} options.width 线宽,默认3,参数可选
* @param {number} options.duration 动画持续时间,默认1000,参数可选
* @example
* this.flashBounds([0,0,10,10]);
*/
flashBounds(bounds, options) {
options = options || {};
return new Promise((resolve => {
if (Array.isArray(bounds) && bounds.length === 4 && typeof bounds[0] === "number") {
bounds = vjmap.GeoBounds.fromArray(bounds);
} else {
bounds = vjmap.GeoBounds.fromDataExtent(bounds);
}
const routePath = [];
routePath.push(bounds.min);
routePath.push(vjmap.geoPoint([bounds.max.x, bounds.min.y]));
routePath.push(bounds.max);
routePath.push(vjmap.geoPoint([bounds.min.x, bounds.max.y]));
routePath.push(bounds.min);
let geoLineDatas = [];
geoLineDatas.push({
points: this.map.toLngLat(routePath),
properties: {
opacity: 1.0
}
})
let polylines = new vjmap.Polyline({
data: geoLineDatas,
lineColor: options.color || 'yellow',
lineWidth: options.width || 3,
lineOpacity: ['get', 'opacity'],
isHoverPointer: false,
isHoverFeatureState: false
});
polylines.addTo(this.map);
vjmap.createAnimation({
from: 1,
to: 10,
duration: options.duration || 1000,
onUpdate: (e) => {
const data = polylines.getData();
data.features[0].properties.opacity = parseInt(e) % 2 ? 1.0 : 0 ;
polylines.setData(data);
},
onStop: () => {
polylines.remove()
resolve({})
},
onComplete: () => {
polylines.remove()
resolve({})
}
})
}))
}
/**
* /**
* @returns {Object} 返回一个对象,包含三个布尔值,分别表示Shift、Ctrl、Alt键是否被按下
* @example
* // 示例一
* // 当前Shift键是否按下
* let key = this.isShiftCtrlAltKeyPressed();
* if (key.shift) {
* console.log("Shift键被按下");
* }
* // 示例二
* // 监听地图单击事件,如果Ctrl键被按下,获取点击的CAD图几何坐标位置,根据位置弹出数据信息框,标题为"点击位置",以表单形式展示
* this.listenMapEvent("click", async ev => {
* // 获取当前是否按下Ctrl键
* let key = this.isShiftCtrlAltKeyPressed();
* if (key.ctrl) {
* if (ev && ev.point) {
* // 获取当前地图的像素坐标,转换为CAD图几何坐标
* const point = this.pixelPointToGeoPoint(ev.point);
* // 根据位置弹出数据信息框,标题为"点击位置",以表单形式展示
* await this.showDataInfoDialog(point, true, "点击位置");
* }
* }
* }, true);
*/
isShiftCtrlAltKeyPressed() {
let shift = false, ctrl = false, alt = false;
if (this.context.global.__listenKeyEvent) {
shift = this.context.global.__listenKeyEvent.isShiftKey();
ctrl = this.context.global.__listenKeyEvent.isCtrlKey();
alt = this.context.global.__listenKeyEvent.isAltKey();
}
return {
shift: shift,
ctrl: ctrl,
alt: alt,
};
}
/**
* 允许或禁止地图旋转、倾斜、滚轮缩放、平移、双击缩放
* @param {boolean} options.rotation 是否允许旋转(参数可选)
* @param {boolean} options.pitch 是否允许倾斜(参数可选)
* @param {boolean} options.zoom 是否允许滚轮缩放(参数可选)
* @param {boolean} options.pan 是否允许平移(参数可选)
* @param {boolean} options.dblclickZoom 是否允许双击缩放(参数可选)
* @example
* // 禁止地图旋转、倾斜、双击缩放, 允许滚轮缩放
* this.setMapInteraction({rotation: false, pitch: false, zoom: true, dblclickZoom: false});
*/
setMapInteraction(options) {
options = options || {};
if (options.rotation === true) {
this.map.dragRotate.enable();
this.map.touchZoomRotate.enable();
this.map.keyboard.enableRotation();
} else if (options.rotation === false) {
this.map.dragRotate.disable();
this.map.touchZoomRotate.disable();
this.map.keyboard.disable();
}
if (options.pitch === true) {
this.map.setMinPitch(this.map.getPitch());
this.map.setMaxPitch(this.map.getPitch());
} else if (options.pitch === false) {
this.map.setMinPitch(0);
this.map.setMaxPitch(90);
}
if (options.zoom === true) {
this.map.scrollZoom.enable();
this.map.touchZoomRotate.enable();
} else if (options.rotation === false) {
this.map.scrollZoom.disable();
this.map.touchZoomRotate.disable();
}
if (options.dblclickZoom === true) {
this.map.doubleClickZoom.enable();
} else if (options.dblclickZoom === false) {
this.map.doubleClickZoom.disable();
}
if (options.pan === true) {
this.map.dragPan.enable();
this.map.touchZoomRotate.enable();
} else if (options.pan === false) {
this.map.dragPan.disable();
this.map.touchZoomRotate.disable();
}
}
/**
* 创建div覆盖物图层
* @param {[number,number,number,number] | [[number,number],[number,number]] | [[number,number],[number,number],[number,number],[number,number]]} data 范围坐标,格式为左上角和右下角坐标[x1,y1,x2,y2] 或 [[x1,y1],[x2,y2]],或四个角坐标[[x1,y1],[x2,y2],[x3,y3],[x4,y4]]
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {number} options.width div元素的像素宽度
* @param {number} options.height div元素的像素高度
* @param {string} options.customImage 图片名称
* @param {string} options.customHtml 自定义Html元素的JavaScript代码,返回创建的DOM元素
* @param {number} options.minZoom 图层显示的最小缩放级别,范围0-24
* @param {number} options.maxZoom 图层显示的最大缩放级别,范围0-24
* @param {number} options.maxPitch 图层显示的最大倾斜角度,范围0-90
* @param {boolean} options.updateDivSize 是否自动更新div大小,对SVG放大特别有用
* @param {number} options.maxDivSize div元素允许的最大像素尺寸,超过此值将使用像素放大
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 采集一个矩形坐标,根据此矩形范围坐标,创建图层名称为layer30的div覆盖物图层,图片名称为picture1
* const rect = await this.actionDrawRectangle();
* if (!rect) return;
* let layer = await this.createDivOverlayLayer(rect,{
* layerName: 'layer30',
* customImage: 'picture1'
* });
* console.log(layer);
*/
async createDivOverlayLayer(data, options) {
options = options || {};
let type = "divOverlay";
let randomId = vjmap.RandomID();
let layerId = `layer_${type}_${randomId}`;
options.bounds = data;
if (options.customImage) {
let image = this.context.ui.getCustomImages(options.customImage);
if (image) {
options.customImage = image.value;
options.width = image.width;
options.height = image.height;
}
}
if (Array.isArray(options.bounds) && options.bounds.length == 4 && typeof options.bounds[0] == 'number') {
options.bounds = vjmap.GeoBounds.fromArray(options.bounds).toPointArray();
}
let layerOptions = {
layerId,
...options,
type: type
}
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId: undefined, layerName, data: undefined, options, layer, visible: true, create: this.createDivOverlayLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId: undefined,
layerId,
layerName
};
}
/**
* 手动交互式绘制一个圆
* @param {object} options 配置参数, 参数可选
* @param {boolean} options.isSnap 是否启用捕捉功能, 参数可选,默认false
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {boolean} options.isFill 是否填充,默认false,参数可选
* @param {number} options.opacity 透明度,默认1,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 手动绘制一个圆
* const circle = await this.drawCircle({
* isSnap: true,
* color: "#FF0000",
* isFill: true,
* opacity: 0.8
* });
* if (!circle) return;
* console.log("圆ID:", circle.id);
* console.log("圆心:", circle.center);
* console.log("半径:", circle.radius);
*/
async drawCircle(options) {
let circle = await this.actionDrawCircle(options.isSnap);
if (!circle) return;
return this.createCircle(circle.center, circle.radius, options);
}
/**
* 导出当前图形指定范围的图片
* @param {[number, number, number, number]} bounds 范围,格式为[minx, miny, maxx, maxy]
* @param {object} options 导出图片的选项,参数可选
* @param {number} options.pictureWidth 图片宽度,单位像素, 默认1024
* @param {number} options.scale 缩放比例,默认1.01
* @param {boolean} options.transparent 是否透明背景,默认false
* @param {string} options.backgroundColor 背景颜色,默认'rgb(0,0,0)'
* @param {boolean} options.disableOpen 只返回地址,不打开图片,默认false
* @example
* // 把当前地图范围[100, 200, 300, 400]导出为png图片
* this.exportPng([100, 200, 300, 400]);
*/
exportPng(bounds, options) {
options = options || {};
if (Array.isArray(bounds) && bounds.length === 4 && typeof bounds[0] === 'number') {
bounds = vjmap.GeoBounds.fromArray(bounds);
} else {
bounds = vjmap.GeoBounds.fromDataExtent(bounds);
}
bounds = bounds.scale(options.scale || 1.01); // 稍大点,要不边框线有可能刚好看不见了
let pictureWidth = options.pictureWidth || 1024;
let svc = this.map.getService();
let wmsUrl = svc.wmsTileUrl({
width: pictureWidth,
height: Math.round(pictureWidth * bounds.height() / bounds.width()),
srs: "",
bbox: bounds.toString(),
transparent: options.transparent || false,
backgroundColor: options.backgroundColor || 'rgb(0,0,0)'
});
if (!options.disableOpen) {
window.open(wmsUrl);
}
return wmsUrl;
}
/**
* 移除小地图(鹰眼)控件
* @example
* // 移除小地图(鹰眼)控件
* this.removeMiniMapControl();
*/
removeMiniMapControl() {
this.context.global.controls = this.context.global.controls || {};
let options;
if (this.context.global.controls.miniMapControl) {
options = {...this.context.global.controls.miniMapControl.options}
this.map.removeControl(this.context.global.controls.miniMapControl.control);
delete this.context.global.controls.miniMapControl;
}
// 增加撤销记录
this.context.ui.addUndoRecord(() => {
if (options) {
this.addMiniMapControl(options);
}
});
}
/**
* 删除所有功能菜单
* @example
* // 删除所有功能菜单
* this.deleteMenuItems()
*/
deleteMenuItems() {
this.context.ui.deleteMenuItems();
}
/**
* 获取地图的倾斜角度
* @returns {number} 当前地图的倾斜角度
*/
getPitch() { this.map.getPitch() }
/**
* 创建或更新地图,通过图的fileid、url地址或手动选择本地图纸创建或更新地图
* @param {object} options 参数 可选
* @param {string} options.fileid 图纸的fileid, 为空时则手动选择本地图纸, 可以是一个url地址或文件id
* @param {string} options.mapid 地图id 可选,为空时则自动生成mapid
* @param {"Memory" | "GeomRaster" | "GeomVector"} options.openway 打开方式 可选,Memory: 内存, GeomRaster: 栅格, GeomVector: 矢量, 默认栅格
* @param {boolean} options.lightTheme 是否使用浅色主题 可选,为空时则使用深色主题
* @param {boolean} options.isFullExtent 是否全图范围显示 可选,默认false
* @returns {Object | undefined} 返回对象,包含地图id和版本和fileid {mapid: string, version: string, fileid: string}, 如果取消则返回undefined
* @example
* // 手动选择本地图纸创建地图
* await this.createOrUpdateMap();
* // 通过url地址创建地图,url地址为https://vjmap.com/static/assets/data/gym.dwg, mapid为newgym,以矢量方式打开, 使用深色主题
* await this.createOrUpdateMap({
* fileid: "https://vjmap.com/static/assets/data/gym.dwg",
* mapid: "newgym",
* openway: "GeomVector",
* lightTheme: false
* });
*/
async createOrUpdateMap(options) {
options = options || {};
let fileid = options.fileid || "";
let svc = this.map.getService();
let uploadname;
if (!fileid) {
let formats = await svc.cmdGetSupportFormat();
let acceptFormat = '';
if (formats.cad) {
acceptFormat = formats.cad.split(";").map(f => '.' + f).join(",")
}
if (formats.image) {
acceptFormat += "," + formats.image.split(";").map(f => '.' + f).join(",")
}
const selectFile = () => {
return new Promise((resolve, reject) => {
// 创建一个隐藏的文件输入元素
const input = document.createElement('input');
input.type = 'file';
// 设置允许选择的文件类型
input.accept = acceptFormat;
// 添加事件监听器,当用户选择文件时触发
input.addEventListener('change', () => {
if (input.files && input.files.length > 0) {
resolve(input.files[0]); // 返回选中的第一个文件对象
} else {
reject(new Error('No file selected'));
}
});
// 触发文件选择窗口
input.click();
});
}
try {
// 选择文件
let file = await selectFile();
// 上传文件
let res = await svc.uploadMap(file);
if (res && res.error) {
this.showMessage(res.error)
return;
}
fileid = res.fileid;
uploadname = res.uploadname;
}
catch(err) {
this.showMessage(err)
return;
}
}
let mapid = options.mapid || "";
if (!mapid) {
mapid = fileid;
// 如果是url地址
if (fileid.toLowerCase().endsWith('.dwg')) {
mapid = svc.strMd5(fileid).substring(0, 16);
}
}
let mapopenway = options.openway === "Memory" ? vjmap.MapOpenWay.Memory : vjmap.MapOpenWay.GeomRender;
let isVectorStyle = options.openway === "GeomVector" ? true : false;
let lightTheme = options.lightTheme ?? (this.map.getService().currentMapParam()?.darkMode === true ? false : true)
this.showMessage("正在打开地图中,请稍等...", "success")
// 打开地图
let result = await svc.updateMap({
mapid: mapid,
fileid: fileid,
mapopenway: mapopenway,
uploadname: uploadname,
style: lightTheme ? vjmap.openMapLightStyle() : vjmap.openMapDarkStyle(),
});
// 切换成新图
let res = await this.map.switchMap({
mapid: result.mapid,
version: result.version,
mapopenway: mapopenway,
style: lightTheme ? vjmap.openMapLightStyle() : vjmap.openMapDarkStyle()
}, false, isVectorStyle, true, true);
if (options.isFullExtent !== true) {
// 用默认的中心点和缩放级别参数
let svc = this.map.getService();
let param = svc.currentMapParam();
if (param && param.view) {
if (param.view.center) {
this.map.setCenter(param.view.center);
}
if (param.view.zoom) {
this.map.setZoom(param.view.zoom);
}
if (param.view.bearing) {
this.map.setBearing(param.view.bearing);
}
}
}
if (res && res.error) {
this.showMessage(res.error)
return;
}
// 打开新的地图了,需要清空历史记录
this.context.ui.clearUndoRecord();
return {
mapid: result.mapid,
fileid: fileid,
version: result.version
};
}
/**
* 删除CAD图实体对象
* @param {boolean} rectSelect 是否矩形选择, false表示点选,true表示矩形选择, 参数可选, 默认false
* @example
* // 示例一
* // 点选删除CAD图实体对象
* this.deleteCadEntity(false);
* // 示例二
* // 矩形选择删除CAD图实体对象
* this.deleteCadEntity(true);
*/
async deleteCadEntity(rectSelect) {
let draw = this.map.getDrawLayer();
if (!this.context.global.updateMapStyleObj) {
this.context.global.updateMapStyleObj = vjcommon.createUpdateMapStyleObj(this.map);
}
const updateMapStyleObj = this.context.global.updateMapStyleObj;
// 保存旧的数据,用于撤销记录
let oldHideObjectIds = new Set(updateMapStyleObj.hideObjectIds);
const deleteEntitys = vjcommon.getTrackFeatureProperty(draw, "deleteEntitys") || [];
const hideEntityObjectIds =
vjcommon.getTrackFeatureProperty(draw, "hideEntityObjectIds") || [];
const hideEntityIds = vjcommon.getTrackFeatureProperty(draw, "hideEntityIds") || [];
await vjcommon.deleteCadEntity(this.map, draw, updateMapStyleObj, this.map.logInfo.bind(this.map), async (content) => {
return await this.context.ui.confirm(content, "提示", "warning");
}, rectSelect);
let newHideObjectIds = new Set(updateMapStyleObj.hideObjectIds);
let diffObjectIds = newHideObjectIds.difference(oldHideObjectIds);
// 添加撤销记录
this.context.ui.addUndoRecord(async () => {
await updateMapStyleObj.removeHideObjectIds(diffObjectIds);
vjcommon.setTrackFeatureProperty(draw, {
deleteEntitys: Array.from(new Set(deleteEntitys)),
hideEntityObjectIds: Array.from(new Set(hideEntityObjectIds)),
hideEntityIds: Array.from(new Set(hideEntityIds)),
});
});
}
/**
* 复制CAD图实体对象
* @param {boolean} rectSelect 是否矩形选择, false表示点选,true表示矩形选择, 参数可选, 默认false
* @example
* // 示例一
* // 通过点选复制CAD图实体对象
* await this.copyCadEntity(false);
* // 示例二
* // 通过矩形选择复制CAD图实体对象
* await this.copyCadEntity(true);
*/
async copyCadEntity(rectSelect) {
let draw = this.map.getDrawLayer();
if (!this.context.global.updateMapStyleObj) {
this.context.global.updateMapStyleObj = vjcommon.createUpdateMapStyleObj(this.map);
}
const updateMapStyleObj = this.context.global.updateMapStyleObj;
// 保存旧的数据,用于撤销记录
let features = draw.getAll().features;
let oldFeatureLength = features ? features.length : 0;
await vjcommon.copyCadEntity(this.map, draw, updateMapStyleObj, this.map.logInfo.bind(this.map), async (content) => {
return await this.context.ui.confirm(content, "提示", "warning");
}, rectSelect, (content) => {
return this.context.ui.prompt("请输入内容", "提示", content);
});
let addFeatureIds = [];
let newFeatures = draw.getAll().features;
let newFeatureLength = newFeatures ? newFeatures.length : 0;
for (let i = oldFeatureLength; i < newFeatureLength; i++) {
addFeatureIds.push(newFeatures[i].id);
}
// 添加撤销记录
this.context.ui.addUndoRecord(async () => {
this.map.getDrawLayer().delete(addFeatureIds);
});
}
/**
* 采集一条线的坐标
* @param {boolean} isSnap 是否启用捕捉功能, 默认false
* @returns {Promise<number[][] | undefined>} 返回坐标点数组[[x1,y1],[x2,y2],...],如果取消操作则返回undefined
* @example
* const line = await this.actionDrawLineString();
* if (!line) return;
* // 输出线的所有点坐标
* for (let i = 0; i < line.length; i++) {
* let x = line[i][0];
* let y = line[i][1];
* console.log(x, y);
* }
*/
async actionDrawLineString(isSnap) {
this.context.tools.logInfo("请在地图上绘制一条线")
let snapObj = {};
if (isSnap) {
vjcommon.getMapSnapPoints(this.map, snapObj);
}
let drawLine = await vjmap.Draw.actionDrawLineSting(this.map, isSnap ? {
api: {
getSnapFeatures: snapObj
}
} : {});
if (drawLine.cancel) {
return;
}
let coords = drawLine.features[0].geometry.coordinates;
return coords.map(c => {
let p = this.map.fromLngLat(c);
return [p.x, p.y];
});
}
/**
* 手动交互式绘制一个椭圆或椭圆弧
* @param {object} options 配置参数, 参数可选
* @param {boolean} options.isSnap 是否启用捕捉功能, 参数可选,默认false
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {string} options.borderColor 边框颜色,格式为#RRGGBB,参数可选
* @param {boolean} options.arc 是否绘制椭圆弧,默认false,参数可选
* @param {boolean} options.isFill 是否填充,默认false,参数可选
* @param {number} options.width 线宽,默认1,参数可选
* @param {number} options.opacity 透明度,默认1,参数可选
* @param {number} options.borderOpacity 边框透明度,默认1,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 示例一
* // 手动绘制一个填充椭圆,启用捕捉,颜色为红色,透明度0.6,线宽2,边框颜色为黄色,透明度0.8
* const ellipseFill = await this.drawEllipse({
* isSnap: true,
* color: "#FF0000",
* isFill: true,
* opacity: 0.6,
* width: 2,
* borderColor: "#FFFF00",
* borderOpacity: 0.8
* });
* if (ellipseFill && ellipseFill.id) {
* console.log("椭圆ID:", ellipseFill.id);
* }
* // 示例二
* // 手动绘制一个椭圆,颜色为红色,线宽3
* const ellipse = await this.drawEllipse({
* color: "#FF0000",
* isFill: false,
* width: 3,
* });
* if (ellipse && ellipse.id) {
* console.log("椭圆ID:", ellipse.id);
* }
* // 示例三
* // 手动绘制一个椭圆弧,颜色为黄色,线宽2
* const ellipseArc = await this.drawEllipse({
* color: "#FFFF00",
* isFill: false,
* arc: true,
* width: 2,
* });
* if (ellipseArc && ellipseArc.id) {
* console.log("椭圆弧ID:", ellipseArc.id);
* }
*/
async drawEllipse(options) {
options = options || {};
let draw = this.map.getDrawLayer();
let snapObj = {};
if (options.isSnap) {
vjcommon.getMapSnapPoints(this.map, snapObj);
}
let drawOptions = options.isSnap ? {
api: {
getSnapFeatures: snapObj
}
} : {};
let featureId;
if (options.isFill) {
if (options.arc) {
featureId = await vjcommon.drawEllipseFillArc(this.map, draw, drawOptions, {
"color": options.color,// 颜色
"opacity": options.opacity, // 透明度
"outlineColor": options.borderColor || options.color,// 边框颜色
"line_width": options.width, // 线宽
"line_opacity": options.borderOpacity, // 透明度
"data": options.data
}, () => this.map.logInfo);
} else {
featureId = await vjcommon.drawEllipseFill(this.map, draw, drawOptions, {
"color": options.color,// 颜色
"opacity": options.opacity, // 透明度
"outlineColor": options.borderColor || options.color,// 边框颜色
"line_width": options.width, // 线宽
"line_opacity": options.borderOpacity, // 透明度
"data": options.data
}, () => this.map.logInfo);
}
} else {
if (options.arc) {
featureId = await vjcommon.drawEllipseArc(this.map, draw, drawOptions, {
"color": options.borderColor || options.color,// 颜色
"line_width": options.width, // 线宽
"data": options.data
}, () => this.map.logInfo);
} else {
featureId = await vjcommon.drawEllipseEdge(this.map, draw, drawOptions, {
"color": options.borderColor || options.color,// 颜色
"line_width": options.width, // 线宽
"data": options.data
}, () => this.map.logInfo);
}
}
if (featureId) {
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.map.getDrawLayer().delete(featureId);
});
return { id: featureId };
}
}
/**
* 设置数据图层是否可见
* @param {string} layerName 图层名称,参数必填
* @param {boolean} visible 是否可见,参数必填
* @example
* // 设置图层名称为layer1的数据图层隐藏
* let layerName = 'layer1';
* this.setDataLayerVisible(layerName, false);
*/
setDataLayerVisible(layerName, visible) {
let item = this.context.global.layers.find(layer => layer.layerName == layerName);
if(item && item.visible != visible) {
item.visible = visible;
let layerId = item.layerId;
if (item.layer && typeof(item.layer.getLayerId) == "function") {
layerId = item.layer.getLayerId();
}
item.layer.setVisible(this.map, layerId, !visible);
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.setDataLayerVisible(item.layerName, visible);
});
}
}
/**
* 创建调试瓦片图层
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {number} options.rasterOpacity 图片的不透明度,参数可选,取值范围为 0 ~ 1,默认值为 1
* @param {number} options.rasterHueRotate 在色轮上旋转色相的角度,参数可选,默认值为 0,单位:角度
* @param {number} options.rasterBrightnessMin 图片的最小亮度,参数可选,取值范围为 0 ~ 1,默认值为 0
* @param {number} options.rasterBrightnessMax 图片的最大亮度,参数可选,取值范围为 0 ~ 1,默认值为 1
* @param {number} options.rasterSaturation 图片的饱和度,参数可选,取值范围为 -1 ~ 1,默认值为 0
* @param {number} options.rasterContrast 图片的对比度,参数可选,取值范围为 -1 ~ 1,默认值为 0
* @param {number} options.rasterFadeDuration 切换瓦片时的渐隐时间,参数可选,默认值为 300,单位:毫秒
* @param {string} options.rasterResampling 采样方式,参数可选,可选值为 linear、nearest,默认值为 linear
* @param {string} options.color 颜色, 格式为#RRGGBB
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 创建调试瓦片图层
* let layer = await this.createDebugLayer();
* console.log(layer);
*/
async createDebugLayer(options) {
options = options || {};
let type = "raster";
let randomId = vjmap.RandomID();
let sourceId = `source_${type}_${randomId}`;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
sourceId,
layerId,
...options,
type: type
}
let isVector = this.map.hasVectorLayer() ? "&vector=true" : ''
let r = 255, g = 255, b = 0, a = 255;
if (options.color && options.color.startsWith('#') && options.color.length === 7) {
r = parseInt(options.color.slice(1, 3), 16);
g = parseInt(options.color.slice(3, 5), 16);
b = parseInt(options.color.slice(5, 7), 16);
}
let svc = this.map.getService();
let tiles = svc.serviceUrl('this.map/') + `tile/debug/{z}/{x}/{y}?r=${r}&g=${g}&b=${b}&a=${a}${isVector}&token=${svc.accessToken}`;
let sourceData = {
type: "raster",
tiles: [tiles],
tileSize: this.map.hasVectorLayer() ? 512 : 256
}
this.map.addRasterSource(sourceId, sourceData);
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data: sourceData, options, layer, visible: true, create: this.createDebugLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 获取cad图epsg代号
* @param {object} options 配置选项
* @param {string} [options.mapid] 地图ID,为空的话,采用当前打开的地图
* @param {string} [options.version] 地图版本(为空时采用当前打开的地图版本)
* @param {"beijing54"|"xian80"|"cgs2000"} [options.crs] cad图的坐标系,beijing54(北京54坐标)、xian80(西安80坐标、cgs2000(CGCS2000坐标)
* @returns {Promise<string|undefined>} 返回cad图的epsg代号, 如果获取不到,则返回undefined
* @example
* //示例一
* // 获取当前图的epsg代号, 把结果输出至会话窗口
* let epsg = await this.getCadEpsgCode();
* if (epsg) {
* this.chatLog(epsg);
* } else {
* this.chatLog("获取cad图的epsg代号失败");
* }
* //示例二
* // 获取图ca422a931980的epsg代号,cad图的坐标系为西安80
* let epsg2 = await this.getCadEpsgCode({
* mapid: "ca422a931980",
* crs: "xian80"
* });
* console.log(epsg2);
*/
async getCadEpsgCode(options) {
options = options || {}
let svc = this.map.getService();
// 获取中心点,要叠加互联网地图要求是地理坐标X是8位,Y是7位,如果X没有带系的话,X可以是6位
let center = this.map.fromLngLat(this.map.getCenter());
if (options.mapid) {
let metadata = await svc.metadata(options.mapid, options.version);
let mapExtent = vjmap.GeoBounds.fromString(metadata.bounds);
center = mapExtent.center();
}
let strX = parseInt(center.x) + "";
let xLen = strX.length;
let yLen = (parseInt(center.y) + "").length;
if (yLen == 7) {
let crs = vjmap.transform.EpsgCrsTypes.CGCS2000;
if (options.crs == "beijing54") {
crs = vjmap.transform.EpsgCrsTypes.Beijing54;
} else if (options.crs == "xian80") {
crs = vjmap.transform.EpsgCrsTypes.Xian80;
}
if (xLen == 8) {
let epsg = vjmap.transform.getEpsgParam(crs, +strX.substring(0, 2))?.epsg;
if (epsg) {
return epsg;
}
} else if (xLen == 6) {
// 如果是6位的坐,前面没有带号信息,需要在互联网地图上选择地图所有的大概位置的经纬度来确定
let lngLat = await this.context.ui.selectWebMapLngLat({
isGaode: true,
isRoadway: true
})
if (lngLat) {
let res = vjmap.transform.getEpsgCode([lngLat.lng, lngLat.lat]);
let curEpsg;
let curDist;
// 获取最接近的epsg
for(let item of res) {
if (crs == vjmap.transform.EpsgCrsTypes.CGCS2000 && item.crs.indexOf('2000') == -1) {
continue;
} else if (crs == vjmap.transform.EpsgCrsTypes.Beijing54 && item.crs.indexOf('54') == -1) {
continue;
} else if (crs == vjmap.transform.EpsgCrsTypes.Xian80 && item.crs.indexOf('80') == -1) {
continue;
}
let epsg = item.epsg;
let result = await svc.cmdTransform("EPSG:4326", epsg, vjmap.geoPoint([lngLat.lng, lngLat.lat])); // 如果不想调服务转,也可以在前端用proj4库
let pt = result[0];// 返回的第1条结果
let dist = vjmap.geoPoint(pt).distanceTo(vjmap.geoPoint(center))
if (curDist === undefined) {
curDist = dist;
curEpsg = epsg;
} else if (dist < curDist) {
curDist = dist;
curEpsg = epsg;
}
}
if (curEpsg) {
return curEpsg;
}
}
}
}
return undefined;
}
/**
* 修改CAD图实体对象
* @param {boolean} rectSelect 是否矩形选择, false表示点选,true表示矩形选择, 参数可选, 默认false
* @example
* // 示例一
* // 点选修改CAD图实体对象
* this.modifyCadEntity(false);
* // 示例二
* // 矩形选择修改CAD图实体对象
* this.modifyCadEntity(true);
*/
async modifyCadEntity(rectSelect) {
let draw = this.map.getDrawLayer();
if (!this.context.global.updateMapStyleObj) {
this.context.global.updateMapStyleObj = vjcommon.createUpdateMapStyleObj(this.map);
}
const updateMapStyleObj = this.context.global.updateMapStyleObj;
// 保存旧的数据,用于撤销记录
let oldHideObjectIds = new Set(updateMapStyleObj.hideObjectIds);
const deleteEntitys = vjcommon.getTrackFeatureProperty(draw, "deleteEntitys") || [];
const hideEntityObjectIds =
vjcommon.getTrackFeatureProperty(draw, "hideEntityObjectIds") || [];
const hideEntityIds = vjcommon.getTrackFeatureProperty(draw, "hideEntityIds") || [];
let features = draw.getAll().features;
let oldFeatureLength = features ? features.length : 0;
await vjcommon.modifyCadEntity(this.map, draw, updateMapStyleObj, this.map.logInfo.bind(this.map), async (content) => {
return await this.context.ui.confirm(content, "提示", "warning");
}, rectSelect, (content) => {
return this.context.ui.prompt("请输入内容", "提示", content);
});
let newHideObjectIds = new Set(updateMapStyleObj.hideObjectIds);
let diffObjectIds = newHideObjectIds.difference(oldHideObjectIds);
let addFeatureIds = [];
let newFeatures = draw.getAll().features;
let newFeatureLength = newFeatures ? newFeatures.length : 0;
for (let i = oldFeatureLength; i < newFeatureLength; i++) {
addFeatureIds.push(newFeatures[i].id);
}
// 添加撤销记录
this.context.ui.addUndoRecord(async () => {
await updateMapStyleObj.removeHideObjectIds(diffObjectIds);
this.map.getDrawLayer().delete(addFeatureIds);
vjcommon.setTrackFeatureProperty(draw, {
deleteEntitys: Array.from(new Set(deleteEntitys)),
hideEntityObjectIds: Array.from(new Set(hideEntityObjectIds)),
hideEntityIds: Array.from(new Set(hideEntityIds)),
});
});
}
/**
* 获取一个点到多条线的最近点
* @param {[number, number]} point 点坐标[x,y]
* @param {[number, number][][] | geojson} lines 多条线的坐标数组,每个元素是一个包含两个数字的数组[x,y] 或 geojson
* @returns {object} 返回包含最近点、最近点距离、最近点索引、最近点前一个点距离的object, 具体属性如下:
* @example
* // 获取一个点到多条线的最近点, 点坐标[130,140], 多条线坐标[[[100,100],[200,200]],[[100,200],[200,100]]]
* let lines = [
* [[100, 100], [200, 200]],
* [[100, 200], [200, 100]]
* ];
* let point = [130, 140];
* let res = this.closestPointOnPolylines(point, lines);
* console.log(res);
*/
closestPointOnPolylines(point, lines) {
if (lines && lines.type === "FeatureCollection" && lines.features.length > 0) {
let geojsonLines = [];
for(let i = 0; i < lines.features.length; i++) {
if (lines.features[i].geometry.type === "LineString") {
geojsonLines.push(lines.features[i].geometry.coordinates);
} else if (lines.features[i].geometry.type === "MultiLineString") {
lines.features[i].geometry.coordinates.forEach(ln => geojsonLines.push(ln));
}
}
lines = geojsonLines;
}
if (Array.isArray(lines)) {
lines = lines.map(line => line.map(co => vjmap.geoPoint(co)));
}
// 计算与所有线的最近点
const res = vjmap.closestPointOnPolylines(vjmap.geoPoint(point), lines);
if (res && res.closestPoint) {
res.closestPoint = [res.closestPoint.x, res.closestPoint.y];
}
return res;
}
/**
* 通过数据获取数据范围大小
* @param {Object} geojson 要获取范围的geojson数据,参数必填
* @returns {[number, number, number, number]} 返回包含四个数字的数组[x1,y1,x2,y2],表示geojson数据范围的左下角和右上角坐标
* @example
* // 获取geojson数据范围大小
* let geojson = {type: "FeatureCollection", features: [{type: "Feature", geometry: {type: "Point", coordinates: [10, 20]}}]};
* let bounds = this.getDataGeoBounds(geojson);
* console.log(bounds);
*/
getDataGeoBounds(geojson) {
let dataExtent = vjmap.GeoBounds.fromDataExtent(geojson);
return dataExtent.toArray();
}
/**
* 手动交互式绘制一个矩形
* @param {object} options 配置参数, 参数可选
* @param {boolean} options.isSnap 是否启用捕捉功能, 参数可选,默认false
* @param {string} options.color 颜色,格式为#RRGGBB,参数可选
* @param {boolean} options.isFill 是否填充,默认false,参数可选
* @param {number} options.opacity 透明度,默认1,参数可选
* @param {number} options.data 数据值,参数可选
* @returns {Promise<Object | undefined>} 返回绘制的图元对象,如果取消操作则返回undefined,返回的对象包含以下属性:
* @example
* // 手动绘制一个矩形
* const rect = await this.drawRectangle({
* isSnap: true,
* color: "#FF0000",
* isFill: true,
* opacity: 0.8
* });
* if (!rect) return;
* console.log("矩形ID:", rect.id);
* console.log("矩形坐标:", rect.points);
*/
async drawRectangle(options) {
let points = await this.actionDrawRectangle(options.isSnap);
if (!points) return;
return this.createRectangle(points, options);
}
/**
* 移除一个功能菜单
* @param {string} name 菜单名
* @example
* // 移除菜单名为全图显示的功能菜单
* this.removeMenuItem("全图显示")
*/
removeMenuItem(name, func) {
this.context.ui.removeMenuItem(name, func);
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.addMenuItem(name, func);
});
}
/**
* 选择地图, 返回选择的地图mapid和version
* @returns {Object | undefined} 返回对象,包含地图id和版本 {mapid: string, version: string}, 如果取消则返回undefined
* @example
* // 选择地图, 返回选择的地图mapid和version
* let res = await this.selectCadMap();
* if (res) {
* this.chatLog("选择的地图mapid为" + res.mapid + ", version为" + res.version);
* }
*/
async selectCadMap() {
let res = await this.context.ui.selectCadMap()
if (!res) return;
return {
mapid: res.mapid,
version: res.version
};
}
/**
* 创建动画多边形数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @param {string} options.fillColor 填充颜色
* @param {string} options.fillOutlineColor 边框颜色
* @param {number} options.fillOpacity 填充透明度,取值范围0-1
* @param {string} options.fillPattern 填充图案,必须与地图已加载的图片名称对应,图像宽度必须是2的系数(2、4、8、...、512)
* @param {[number,number]} options.fillTranslate 几何体的平移量[x,y],负数分别表示左和上,单位像素,默认[0,0]
* @param {string} options.fillTranslateAnchor 平移参考系,"map"表示相对于地图平移,"viewport"表示相对于视窗平移
* @param {boolean} options.fillAntialias 是否启用填充反锯齿
* @param {string} options.animateImagesType 动画类型,可选值:"arrow"(箭头动画), "antpath"(蚂蚁线动画), "customImage"(自定义图片), "imageSprites"(同一个图片精灵动画集)
* @param {string} options.animateImages_arrowFillColor 箭头填充颜色,当animateImagesType为"arrow"时有效,默认"#22B14C"
* @param {string} options.animateImages_arrowStrokeColor 箭头边框颜色,当animateImagesType为"arrow"时有效,默认"#fff"
* @param {number} options.animateImages_arrowStrokeWidth 箭头线宽,当animateImagesType为"arrow"时有效
* @param {number} options.animateImages_arrowWidth 箭头大小,当animateImagesType为"arrow"时有效
* @param {string} options.animateImages_fillColor1 蚂蚁线填充颜色1,当animateImagesType为"antpath"时有效
* @param {string} options.animateImages_fillColor2 蚂蚁线填充颜色2,当animateImagesType为"antpath"时有效
* @param {string} options.customImageUrl 自定义图片URL或base64,当animateImagesType为"customImage"时有效
* @param {string} options.imageSpriteUrl 图片动画集URL或base64,当animateImagesType为"imageSprites"时有效
* @param {number} options.animateImages_spriteWidth 来源图片中精灵图所占宽度,当animateImagesType为"imageSprites"时有效
* @param {number} options.animateImages_spriteHeight 来源图片中精灵图所占高度,当animateImagesType为"imageSprites"时有效
* @param {number} options.animateImages_canvasWidth 画布宽度,所有动画类型通用
* @param {number} options.animateImages_canvasHeight 画布高度,所有动画类型通用
* @param {number} options.animateImages_frameCount 动画帧数,所有动画类型通用
* @param {boolean} options.animateImages_directionReverse 动画方向是否反向,所有动画类型通用
* @param {number} options.speed 动画速度,步长0.1
* @param {boolean} options.startAutoAnimation 是否开始时自动播放动画
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer2的动画多边形数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createAnimateFillLayer(geojson, {
* layerName: 'layer2'
* });
* console.log(layer);
*/
async createAnimateFillLayer(data, options) {
options = options || {};
let type = "animateFill";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
fillColor: "#00ffff",
fillOutlineColor: "#00ff00",
animateImagesType: "arrow",
animateImages_arrowFillColor: "#22B14C",
animateImages_arrowStrokeColor: "#fff",
animateImages_arrowStrokeWidth: 6,
animateImages_canvasWidth: 128,
animateImages_canvasHeight: 32,
animateImages_arrowWidth: 16,
animateImages_frameCount: 4,
animateImages_fillColor1: "#f0fb",
animateImages_fillColor2: "#0ffb",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createSymbolCircleLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 获取CAD图层列表信息
* @param {string} mapid 地图id 可选,为空时则获取当前打开的地图
* @param {string} version 版本 可选,为空时则获取当前打开的地图版本
* @returns {Promise<Object[]>} 返回CAD图层列表信息, 每项的属性如下
* @example
* // 示例一
* // 获取CAD图层列表信息
* let res = await this.getCadLayers();
* if (res) {
* this.chatLog("图层名称为:" + res.map(layer => layer.name).join(","));
* }
* // 示例二
* // 获取CAD图sys_world的v1版本图层列表信息
* let resp = await this.getCadLayers("sys_world", "v1");
* if (resp) {
* console.log(resp);
* }
*/
async getCadLayers(mapid, version) {
let svc = this.map.getService();
if (!mapid) {
return svc.getMapLayers();
} else {
// 先获取元数据
let meta = await svc.metadata(mapid, version);
if (meta && meta.layers) {
return meta.layers;
}
}
}
/**
* 删除所有功能按钮
* @example
* // 删除所有功能按钮
* this.deleteButtonItems()
*/
deleteButtonItems() {
this.context.ui.deleteButtonItems();
}
/**
* 设置绘制图层的图元数据
* @param {<GeoJSON | undefined>} data 图元数据
*/
setDrawLayerData(data) {
let draw = this.map.getDrawLayer();
draw.set(this.map.toLngLat(data));
}
/**
* 创建符号圆数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {number} options.circleRadius 圆半径,单位像素,参数可选
* @param {string} options.circleColor 圆填充颜色,支持十六进制颜色值,参数可选
* @param {number} options.circleOpacity 圆填充透明度,取值范围0-1,参数可选
* @param {string} options.circleStrokeColor 圆边框颜色,支持十六进制颜色值,参数可选
* @param {number} options.circleStrokeWidth 圆边框宽度,单位像素,参数可选
* @param {number} options.circleStrokeOpacity 圆边框透明度,取值范围0-1,参数可选
* @param {number} options.circleBlur 圆模糊度,参数可选
* @param {[number]} options.circleTranslate 平移量,格式[x,y],负数表示向左和向上平移,单位像素,默认[0,0],参数可选
* @param {string} options.circleTranslateAnchor 平移参考系,'map'表示相对地图平移,'viewport'表示相对视窗平移,参数可选
* @param {string} options.circlePitchScale 倾斜时缩放对齐方式,'map'相对地图,'viewport'相对视窗,参数可选
* @param {string} options.circlePitchAlignment 倾斜时对齐方式,'map'相对地图,'viewport'相对视窗,参数可选
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer7的圆数据图层,圆半径为5,颜色为红色
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createSymbolCircleLayer(geojson, {
* layerName: 'layer7',
* circleRadius: 5,
* circleColor: "#ff0000"
* });
* console.log(layer);
*/
async createSymbolCircleLayer(data, options) {
options = options || {};
let type = "circle";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
"circleRadius": 5,
"circleColor": "#ffff00",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createSymbolCircleLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 获取当前显示地图预览的url地址
* @returns {Promise<string>} 返回当前显示地图的url地址
* @example
* // 获取当前显示地图预览的url地址
* let url = await this.curMapPreviewUrl();
* console.log(url);
*/
curMapPreviewUrl() {
let svc = this.map.getService();
let mapParam = svc.currentMapParam();
let url = svc.baseUrl() + "_mapviewer?";
let param = {};
param["render"] = 1; // 默认几何栅格
if (mapParam?.mapopenway == vjmap.MapOpenWay.Memory) {
param["render"] = 0; // 内存直接打开
} else if (mapParam?.mapopenway == vjmap.MapOpenWay.GeomRender && window.location.href.indexOf("vector=true") > 0) {
param["render"] = 2; // 默认几何矢量
}
param["mapid"] = mapParam?.mapid;
param["version"] = mapParam?.version;
param["layer"] = mapParam?.layer;
param["centerX"] = this.map.getCenter().lng;
param["centerY"] = this.map.getCenter().lat;
param["zoom"] = this.map.getZoom();
param["pitch"] = this.map.getPitch();
param["bearing"] = this.map.getBearing();
param["light"] = mapParam?.darkMode !== false;
param["title"] = `唯杰地图vjmap-图名称:${mapParam?.mapid}-版本:${mapParam?.version}-样式:${mapParam?.layer}`;
param["mobile"] = true;
param['token'] = svc.accessToken;
if (svc.getCurWorkspaceName()) {
param['workspace'] = svc.getCurWorkspaceName();
}
url = url + Object.keys(param).map(item => item + "=" + param[item]).join("&");
return url;
}
/**
* 目标匹配
* @param {object} options 参数
* @param {string} [options.mapid] 地图ID
* @param {string} [options.version] 地图版本(为空时采用当前打开的地图版本)
* @param {number} [options.selectWay] 选择方式. 0表示框选实体,1表示点选实体, 2不手动选择实体,默认0
* @param {string} [options.layer] 图层样式名 为空时,将由选择的实体的图层来决定
* @param {string} [options.mapBounds] 目标匹配的地图范围,默认为全图的数据范围
* @param {string} [options.objectIds] 目标匹配的实体objectid数组,用||隔开
* @param {string} [options.objectBounds] 目标匹配的实体范围
* @param {string} [options.layeron] 目标匹配的地图打开的图层
* @param {number} [options.size] 匹配时的图像尺寸,默认10000
* @param {string} [options.method] 方法. 默认 matchPattern
* @param {number} [options.maxCount] 允许返回的最大条数. 默认 200
* @param {number} [options.score] 分数,小于此分类将不显示 (0 -1),默认 0.6
* @param {number} [options.minReduceArea] 金字塔大小 范围(64-2048).默认256, 方法为matchPattern有效
* @param {boolean} [options.canOverlap] 允许重叠 (默认false)
* @param {number} [options.maxOverlap] 重叠比例 范围(0-0.8),默认 0.3
* @param {number} [options.toleranceAngle] 角度范围(-180, 180)有旋转的时候需要。方法为matchPattern有效. 默认180
* @param {boolean} [options.useToleranceRange] 是否使用角度区间范围,默认false. 方法为matchPattern有效
* @param {number} [options.tolerance1] 角度区间1开始角度. 方法为matchPattern有效
* @param {number} [options.tolerance2] 角度区间1结束角度. 方法为matchPattern有效
* @param {number} [options.tolerance3] 角度区间2开始角度. 方法为matchPattern有效
* @param {number} [options.tolerance4] 角度区间2结束角度. 方法为matchPattern有效
* @param {boolean} [options.showSettingForm] 是否弹出设置对话框. 默认true
* @returns {Promise<GeoJSON>} 返回比较结果, 如果失败则返回undefined, 如果成功则返回匹配的GeoJSON数据
* @example
* // 点选实体对当前地图进行目标匹配
* let res = await this.matchObject({
* selectWay: 1
* });
* console.log(res);
*/
async matchObject(options) {
options = options || {};
let svc = this.map.getService();
options = {
mapid: options.mapid || svc.currentMapParam().mapid,
objectIds: '',
objectBounds: '',
method: "matchPattern",
maxCount: 200,
score: 0.6,
size: 10000,
canOverlap: false,
maxOverlap: 0.3,
toleranceAngle: 180,
layer: "",
layeron: "",
...options,
}
if (options.selectWay === 0) {
let isPointSel = options.selectWay === 1;
let param = {};
let res = await vjcommon.selectFeatures(this.map, true, isPointSel, isPointSel, true);
vjcommon.clearHighlight(this.map);
if (!res || res.length == 0) return;
const objectIds = res.map(f => f.properties.objectid);
const layeron = `(${Array.from(new Set(res.map(f => f.properties.layerindex))).join(",")})`;
const features = {
type: "FeatureCollection",
features: res
}
let data = this.map.fromLngLat(features);
let bounds = vjcommon.getGeoJsonBounds(data).scale(1.01); // 稍放大点
param.objectIds = objectIds.join("||");
param.objectBounds = `[${bounds.toString()}]`;
param.layeron = layeron;
options = {
...options,
...param
}
}
if (options.showSettingForm !== false) {
options = await this.showDataModifyDlg({
data: options,
title: "目标匹配设置",
showForm: true
})
if (!options) return;
}
let result = await svc.cmdMatchObject(options);
if (result && result.error) {
if (this.map.logInfo) this.map.logInfo(result.error, "error", 10000);
return;
}
if (!result || !result.match) return;
let match = result.match.split(";");
let geojson = {
type: "FeatureCollection",
features: []
};
for(let i = 0; i < match.length; i++) {
if (match[i].indexOf(",") <= 0) continue;
const items = match[i].split(",");
const pts = [[+items[0], +items[1]], [+items[2], +items[3]], [+items[4], +items[5]], [+items[6], +items[7]]];
pts.push(pts[0])
geojson.features.push({
type: "Feature",
geometry: {
type: "LineString",
coordinates: pts
}
})
}
return geojson;
}
/**
* 弹出消息输入框
* @param {string} message 对话框内容,参数可选
* @param {string} title 对话框标题,参数可选
* @param {string} inputValue 初始值,参数可选
* @param {boolean} isPassword 是否为密码输入框,参数可选,默认false
* @returns {string|undefined} 返回输入的内容, 如果用户点击取消,则返回undefined
* @example
* // 弹出消息输入框, 消息为"请输入内容", 初始值为空
* let res = await this.messageBoxPrompt("请输入内容", "提示", "");
* if (res !== undefined) {
* console.log("输入的内容为:", res)
* } else {
* console.log("点击了取消")
* }
*/
async messageBoxPrompt(message, title, inputValue, isPassword) {
return await this.context.ui.prompt(message || "请输入内容", title || "提示", inputValue || "", isPassword || false);
}
/**
* 获取所有地图数据或通过指定mapid获取地图数据
* @param {string} mapid 地图ID. 可选,为空时则获取所有地图数据
* @param {string} version 版本号. 可选,为空时则获取最新版本, *表示所有版本
* @returns {Promise<Array<Object>>} 返回地图元数据, 如果失败则返回undefined, 数据每项的字段如下:
* @example
* // 示例一
* // 获取所有地图数据
* let res = await this.listMaps();
* console.log(res);
* // 示例二
* // 获取地图sys_hello的所有版本数据
* let res2 = await this.listMaps("sys_hello", "*");
* console.log(res2);
*/
async listMaps(mapid, version) {
let svc = this.map.getService();
let res = await svc.listMaps(mapid, version);
let result = [];
if (mapid) {
if (res && typeof res["version"] === "string") {
result.push({
mapid: mapid,
...res
});
return result;
}
}
for(let id in res) {
let data = res[id];
result.push({
mapid: id,
...data
});
}
return result;
}
/**
* 投影prj文件WKT字符转proj4字符串
* @param {string} wkt 投影prj文件WKT字符
* @param {string} [from] 字符串来源,为空为话为WKT
* @returns {Promise<string>} 返回proj4字符串
* @example
* // 将WKT字符串`PROJCS["CGCS2000 / 3-degree Gauss-Kruger CM 111E",GEOGCS["China Geodetic Coordinate System 2000",DATUM["China_2000",SPHEROID["CGCS2000",6378300,300,AUTHORITY["EPSG","1024"]],AUTHORITY["EPSG","1043"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.018,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4490"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",112.1],PARAMETER["scale_factor",1],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","4546"]]`转换为proj4字符串
* let proj4String = await this.prjWktToPrj4(`PROJCS["CGCS2000 / 3-degree Gauss-Kruger CM 111E",GEOGCS["China Geodetic Coordinate System 2000",DATUM["China_2000",SPHEROID["CGCS2000",6378300,300,AUTHORITY["EPSG","1024"]],AUTHORITY["EPSG","1043"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.018,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4490"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",112.1],PARAMETER["scale_factor",1],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","4546"]]`);
* console.log(proj4String);
*/
async prjWktToPrj4(wkt, from) {
let svc = this.map.getService();
let res = await svc.cmdPrjWktToPrj4(wkt, from);
return res?.proj4;
}
/**
* 移除鼠标位置控件
* @example
* // 移除鼠标位置控件
* this.removeMousePositionControl();
*/
removeMousePositionControl() {
this.context.global.controls = this.context.global.controls || {};
let options;
if (this.context.global.controls.mousePositionControl) {
options = {...this.context.global.controls.mousePositionControl.options}
this.map.removeControl(this.context.global.controls.mousePositionControl.control);
delete this.context.global.controls.mousePositionControl;
}
// 增加撤销记录
this.context.ui.addUndoRecord(() => {
if (options) {
this.addMousePositionControl(options);
}
});
}
/**
* 关闭数据显示面板
* @example
* // 关闭数据显示面板
* this.closeDataPanel()
*/
closeDataPanel() {
this.context.ui.setDataPanelContent();
}
/**
* 创建点标记聚合数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {string} options.text 要显示的文字,参数可选,默认为"index ${props.index}"
* @param {number} options.minZoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxZoom 显示最大缩放级别,参数可选,默认为24
* @param {boolean} options.isDrawLinePoint 绘制点时包括线上所有点, 参数可选,默认为false
* @param {string} options.color 标记颜色,参数可选,默认为'#3FB1CE'
* @param {number[]} options.offset 偏离XY像素值,如[0,0],负数表示向左和向上
* @param {number} options.rotation 标记旋转角度,顺时针旋转,默认0
* @param {string} options.anchor 标记锚点位置,可选值:center、top、bottom、left、right、top-left、top-right、bottom-left、bottom-right,默认center
* @param {boolean} options.draggable 是否可拖动,参数可选,默认为false
* @param {string} options.animationType 动画类型:MAP_ANIMATION_NONE(无动画)、MAP_ANIMATION_BOUNCE(弹跳)、MAP_ANIMATION_DROP(坠落)
* @param {string} options.rotationAlignment 旋转对齐:auto(自动)、map(相对地图)、viewport(相对视口),默认auto
* @param {string} options.pitchAlignment 倾斜对齐:auto(自动)、map(相对地图)、viewport(相对视口),默认auto
* @param {number} options.scale 缩放比例,默认1,对应高度41px宽度27px
* @param {number} options.scaleMaxZoom 能缩放的最大级别(0-24),小于此级别时自动缩放
* @param {number} options.height 标高值,单位CAD图制图单位
* @param {boolean} options.removeWhenNoInMapView 是否在视图范围内自动显隐
* @param {number} options.removeWhenNoInMapViewPadding 视图范围内自动显隐的padding像素值,默认500
* @param {number} options.clickTolerance 点击像素容差,默认继承地图的clickTolerance
* @param {string} options.customImage 自定义图片地址/base64/svg内容
* @param {number} options.customImageWidth 自定义图片宽度
* @param {number} options.customImageHeight 自定义图片高度
* @param {string} options.customHtml 自定义Html元素的JS代码
* @param {string} options.popupHtml 信息窗口HTML内容
* @param {[number,number]} options.popupOffset 信息窗口偏离XY像素值
* @param {string} options.popupAnchor 信息窗口锚点位置,同anchor参数
* @param {boolean} options.closeButton 是否显示关闭按钮
* @param {boolean} options.closeOnClick 是否在点击地图时关闭弹出窗口
* @param {boolean} options.closeOnMove 是否在地图移动后关闭信息窗口
* @param {boolean} options.focusAfterOpen 是否尝试聚焦弹出窗口内第一个可聚焦元素
* @param {string} options.className 添加到弹出容器的CSS类名
* @param {string} options.maxWidth 弹出窗口最大宽度,CSS属性字符串,默认'240px'
* @param {boolean} options.allowOverlap 是否允许标记重叠
* @param {number} options.allowOverlapMaxZoom 允许重叠的最大缩放级别,小于或等于此级别才会处理重叠,超过此级时会全部显示,默认4级
* @param {number} options.markerWidth marker div的像素宽,用于计算重叠时需要,默认40
* @param {number} options.markerHeight marker div的像素高,用于计算重叠时需要,默认40
* @param {Object} options.textStyle 聚合文本的CSS样式对象,如{cursor:"pointer", opacity:0.8等}
* @param {Array<Array>} options.textColors 聚合文本颜色配置,格式为[[值1,文本颜色,背景色], [值2,文本颜色,背景色], ...]
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer20的点标记聚合数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createMarkerClusterLayer(geojson, {
* layerName: 'layer20'
* });
* console.log(layer);
*/
async createMarkerClusterLayer(data, options) {
options = options || {};
let type = "markerCluster";
if (options.height) {
options.height = this.map.geoLengthToHeight(options.height);
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
textStyle: {
cursor: "pointer",
opacity: 0.8,
padding: "6px",
"border-radius": "12px",
"border-width": 0,
"box-shadow": "0px 2px 6px 0px rgba(97,113,166,0.2)",
"text-align": "center",
"font-size": "14px",
},
textColors: [
[0, "#00FFFF", "#00FFFF"],
[5, "#F0F", "#80FF00"],
[10, "#00F", "#FFFF00"],
[1000, "#FFF", "#FF3D3D"]
],
popupHtml: "<h3 style='color:red'>index ${props.index}</h3>",
popupOffset: [0, -28],
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createMarkerClusterLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 创建矢量瓦片图层
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {string} options.tileMapId 瓦片的mapid,参数可选
* @param {string} options.tileMapVersion 瓦片的版本,参数可选
* @param {string} options.tileMapLayer 瓦片的样式图层名称,参数可选
* @param {string|Array<string>} options.tiles 瓦片地址,支持变量 {x},{y},{z},{serviceUrl},{accessToken}
* @param {number} options.tileSize 瓦片大小,参数可选,默认值为 512
* @param {number[]} options.bounds 瓦片范围,参数可选,格式为[minx, miny, maxx, maxy],默认为全图范围
* @param {string} options.scheme 瓦片y方向,参数可选,可选值为 xyz(Y从顶部开始)、tms(Y从底部开始),默认为 xyz
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 创建矢量瓦片图层, 瓦片的mapid为sys_hello
* let layer = await this.createVectorLayer({
* tileMapId: "sys_hello",
* });
* console.log(layer);
*/
async createVectorLayer(options) {
options = options || {};
let type = "vector";
let randomId = vjmap.RandomID();
let sourceId = `source_${type}_${randomId}`;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
sourceId,
layerId,
...options,
type: type
}
if (options.tileMapId && !options.tiles) {
let layer = options.tileMapLayer;
if (!layer) {
let style = await this.map.getService().createStyle({
backcolor: this.map.getService().currentMapParam().darkMode ? 0x000000 : 0xFFFFFF
}, options.tileMapId, options.tileMapVersion);
layer = style.stylename
}
let tileUrl = this.map.getService().vectorTileUrl({
mapid: options.tileMapId,
version: options.tileMapVersion,
layer: layer,
})
options.tiles = tileUrl;
}
if (options.bounds) {
const p1 = this.map.toLngLat([options.bounds[0], options.bounds[1]]);
const p2 = this.map.toLngLat([options.bounds[2], options.bounds[3]]);
options.bounds = [p1[0], p1[1], p2[0], p2[1]];
}
let sourceData = {
type: "vector",
tiles: Array.isArray(options.tiles) ? options.tiles : [options.tiles],
tileSize: options.tileSize || 512
}
if (options.bounds) {
sourceData.bounds = options.bounds;
}
if (options.scheme) {
sourceData.scheme = options.scheme;
}
this.map.addVectorSource(sourceId, sourceData);
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data: sourceData, options, layer, visible: true, create: this.createVectorLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 创建多边形数据图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {string} options.fillColor 填充颜色
* @param {string} options.fillOutlineColor 边框颜色
* @param {number} options.fillOpacity 填充透明度,取值范围0-1
* @param {string} options.fillPattern 填充图案,必须与地图已加载的图片名称对应,图像宽度必须是2的系数(2、4、8、...、512)
* @param {[number,number]} options.fillTranslate 几何体的平移量[x,y],负数分别表示左和上,单位像素,默认[0,0]
* @param {string} options.fillTranslateAnchor 平移参考系,"map"表示相对于地图平移,"viewport"表示相对于视窗平移
* @param {boolean} options.fillAntialias 是否启用填充反锯齿
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer11的多边形数据图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createFillLayer(geojson, {
* layerName: 'layer11'
* });
* console.log(layer);
*/
async createFillLayer(data, options) {
options = options || {};
let type = "fill";
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
fillColor: "#00ffff",
fillOutlineColor: "#00ff00",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createFillLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
/**
* 创建Geojson点面线图层
* @param {GeoJson} data 数据内容,标准的GeoJson格式,参数必填
* @param {Record<string, any>} options 图层参数选项,参数可选
* @param {string} options.layerName 图层名称,参数可选,默认为空, 如果为空,则使用随机id作为图层名称
* @param {number} options.minzoom 显示最小缩放级别,参数可选,默认为0
* @param {number} options.maxzoom 显示最大缩放级别,参数可选,默认为24
* @param {string} options.fillColor 填充颜色
* @param {string} options.fillOutlineColor 边框颜色
* @param {number} options.fillOpacity 填充透明度,取值范围0-1
* @param {string} options.fillPattern 填充图案,必须与地图已加载的图片名称对应,图像宽度必须是2的系数(2、4、8、...、512)
* @param {[number,number]} options.fillTranslate 几何体的平移量[x,y],负数分别表示左和上,单位像素,默认[0,0]
* @param {string} options.fillTranslateAnchor 平移参考系,"map"表示相对于地图平移,"viewport"表示相对于视窗平移
* @param {boolean} options.fillAntialias 是否启用填充反锯齿
* @param {string} options.lineColor 线条颜色,参数可选,默认为'#ff0000'
* @param {number} options.lineWidth 线宽,参数可选,默认为1
* @param {number} options.lineOpacity 线透明度,参数可选,取值范围0-1
* @param {number} options.lineGapWidth 线间隙宽度,参数可选,默认为1,单位像素
* @param {number} options.lineOffset 线的偏移量,参数可选。正值向右/内偏移,负值向左/外偏移
* @param {number} options.lineBlur 线模糊度,参数可选,需小于线宽度
* @param {[number,number]} options.lineDasharray 虚线样式,参数可选,格式如[1,1]表示实线虚线的组合
* @param {[number,number]} options.lineTranslate 平移量,参数可选,格式如[x,y],负数表示左和上,单位像素
* @param {string} options.lineCap 线帽形状,参数可选,可选值:'butt'(尖头)、'round'(圆头)、'square'(平头)
* @param {string} options.lineJoin 拐角形状,参数可选,可选值:'bevel'(平拐)、'round'(圆拐)、'miter'(棱拐)
* @param {string} options.linePattern 线图案,参数可选,需与预加载的图片名称对应,图像宽度必须是2的幂次
* @param {string} options.lineGradient 线渐变,参数可选
* @param {number} options.circleRadius 圆半径,单位像素,参数可选
* @param {string} options.circleColor 圆填充颜色,支持十六进制颜色值,参数可选
* @param {number} options.circleOpacity 圆填充透明度,取值范围0-1,参数可选
* @param {string} options.circleStrokeColor 圆边框颜色,支持十六进制颜色值,参数可选
* @param {number} options.circleStrokeWidth 圆边框宽度,单位像素,参数可选
* @param {number} options.circleStrokeOpacity 圆边框透明度,取值范围0-1,参数可选
* @param {number} options.circleBlur 圆模糊度,参数可选
* @param {[number,number]} options.circleTranslate 平移量,格式[x,y],负数表示向左和向上平移,单位像素,默认[0,0],参数可选
* @param {string} options.circleTranslateAnchor 平移参考系,'map'表示相对地图平移,'viewport'表示相对视窗平移,参数可选
* @param {string} options.circlePitchScale 倾斜时缩放对齐方式,'map'相对地图,'viewport'相对视窗,参数可选
* @param {string} options.circlePitchAlignment 倾斜时对齐方式,'map'相对地图,'viewport'相对视窗,参数可选
* @param {boolean} options.isHoverPointer 是否启用鼠标悬浮事件,参数可选
* @param {boolean} options.isHoverFeatureState 是否启用悬浮实体状态,参数可选
* @returns {Object} 返回对象,包含 {sourceId: string, layerId: string, layerName: string}
* @example
* // 在地图范围内随机生成GeoJson数据,创建图层名称为layer29的Geojson点面线图层
* let geojson = this.randomGeojsonInMapBounds();
* let layer = await this.createGeojsonDataLayer(geojson, {
* layerName: 'layer29',
* });
* console.log(layer);
*/
async createGeojsonDataLayer(data, options) {
options = options || {};
let type = "geojson";
if (options.entColorToHtmlColor === undefined) {
for(let f of data.features) {
if (f.properties && typeof f.properties.color == "number") {
if (f.properties.color < 0) {
options.entColorToHtmlColor = true;
break;
}
}
}
}
let randomId = vjmap.RandomID();
let sourceId = "source_geojson_" + randomId;
let layerId = `layer_${type}_${randomId}`;
let layerOptions = {
layerId,
sourceId,
fillColor: "#00ffff",
fillOutlineColor: "#00ff00",
lineColor: '#ff0000',
lineWidth: 1,
circleRadius: 5,
circleColor: "#ffff00",
...options,
type: type
}
// 添加数据源
this.map.addGeoJSONSource(sourceId, this.map.toLngLat(data));
// 添加图层
let layer = new vjcommon.providerLayers[layerOptions.type];
await layer.addLayer(this.map, layerOptions);
let layerName = options.layerName || layerId;
this.context.global.layers = this.context.global.layers || [];
this.context.global.layers.push({layerId, sourceId, layerName, data, options, layer, visible: true, create: this.createGeojsonDataLayer});
// 添加撤销记录
this.context.ui.addUndoRecord(() => {
this.deleteDataLayer(layerName);
});
return {
sourceId,
layerId,
layerName
};
}
}
这篇文档有帮助吗?感谢反馈!