import * as ol from 'ol'
import * as style from 'ol/style'
import * as layer from 'ol/layer'
import * as source from 'ol/source'
import * as geom from 'ol/geom'
import * as extent from 'ol/extent'
import * as proj from 'ol/proj'
import * as interaction from 'ol/interaction'
import * as format from "ol/format";
import Modify from "ol/interaction/Modify"
import Draw, {createBox} from "ol/interaction/Draw"
import * as sphere from "ol/sphere";
import {unByKey} from "ol/Observable";
import {formatPosition} from "@/utils/easyMap";
import {isValue} from "@/utils/util";
import {fromCircle} from "ol/geom/Polygon";
import store from "@/store";
const globalLineDash = [
[0, 0], //实线
[15, 15], //长虚线
[5, 5] //虚线
]
const layerFlag = ['layerName', 'drawViewsLayer']
const drawFlag = ['interactionName', 'drawInteraction']
const modifyFlag = ['interactionName', 'modifyInteraction']
const baseDrawConfig = {
// 样式字段
text: null, // 要素上显示的文字,默认无文字
pointIcon: null, // Point的图标,默认圆形
pointScale: 1, // Point的缩放,默认1
pointOffset: [0, 0], // Point的偏移量,默认[0, 0]
lineColor: '#2860F1', // LineString的线段颜色,默认蓝色
lineWidth: 1, // LineString的线段宽度,默认1
lineType: 0, // LineString的线段类型索引,默认0,实线,取globalLineDash数组索引
lineDash: null, // LineString的线段类型,默认null,优先级比lineType高
polyColor: 'rgba(20, 129, 241, 0.1)', // Polygon的填充色,默认蓝色
polyBorderColor: '#2860F1', // Polygon的边框颜色,默认蓝色
polyBorderWidth: 1, // Polygon的边框宽度,默认1
polyBorderType: 0, // Polygon的边框类型索引,默认0,实线,取globalLineDash数组索引
polyBorderDash: null, // Polygon的边框类型,默认null,优先级比polyBorderType高
// 业务字段
show: false, // 标绘样式选项是否显示
featureType: 'Point', // 标绘的要素类型
isPoint: false, // 是否可以标绘点
isLineString: false, // 是否可以标绘线
isPolygon: false, // 是否可以标绘面
showPosition: false, // 是否显示经纬度输入框
refreshStyleFunc: () => {}, // 刷新标绘样式方法
}
export const getBaseDrawConfig = () => {
return JSON.parse(JSON.stringify(baseDrawConfig))
}
/**
*
* @param map
* @param arr 要标绘的数组,每个对象的数据在要素中保存的属性为 'val'
* @param arr > wkt:wkt格式坐标
* @param arr > styles:要素的样式,优先级最高
* @param arr > text:styles为空的时候,要素上显示的文字,默认无文字
* @param arr > textOffsetY:styles为空的时候,要素上显示的文字Y轴偏移量,默认Point-30,其他0
* @param arr > pointIcon:styles为空的时候,Point的图标,默认圆形
* @param arr > pointScale:styles为空的时候,Point的缩放,默认1
* @param arr > pointOffset:styles为空的时候,Point的偏移量,默认[0, 0]
* @param arr > lineColor:styles为空的时候,LineString的线段颜色,默认蓝色
* @param arr > lineWidth:styles为空的时候,LineString的线段宽度,默认1
* @param arr > lineType:styles为空的时候,LineString的线段类型索引,默认0,实线,取globalLineDash数组索引
* @param arr > lineDash:styles为空的时候,LineString的线段类型,默认null,优先级比lineType高
* @param arr > polyColor:styles为空的时候,Polygon的填充色,默认蓝色
* @param arr > polyBorderColor:styles为空的时候,Polygon的边框颜色,默认蓝色
* @param arr > polyBorderWidth:styles为空的时候,Polygon的边框宽度,默认1
* @param arr > polyBorderType:styles为空的时候,Polygon的边框类型索引,默认0,实线,取globalLineDash数组索引
* @param arr > polyBorderDash:styles为空的时候,Polygon的边框类型,默认null,优先级比polyBorderType高
*/
export const drawViews = (map, arr) => {
let _source
const realLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
if (realLayer[0]) {
_source = realLayer[0].getSource()
} else {
_source = new source.Vector(); //图层数据源
const _vector = new layer.Vector({
zIndex: 9999,
source: _source,
});
_vector.set(layerFlag[0], layerFlag[1])
map.addLayer(_vector);
}
_source.clear() // 清空标绘
const features = arr.filter(v => {
try {
new format.WKT().readFeature(v.wkt)
return true
} catch (e) {
console.error('错误坐标:' + v.wkt)
return false
}
}).map(v => {
const feat: any = new format.WKT().readFeature(v.wkt)
const type = feat.getGeometry().getType()
const {
textOffsetY = (type === 'Point' ? -30 : 0),
pointIcon, pointScale, pointOffset,
lineColor, lineWidth, lineType, lineDash,
polyColor, polyBorderColor, polyBorderWidth, polyBorderType, polyBorderDash
} = Object.assign(getBaseDrawConfig(), v)
let styles: any = []
if (v.styles) {
styles = v.styles
} else {
if (type === 'Point') {
if (pointIcon) {
styles.push(new style.Style({
image: new style.Icon({
src: pointIcon,
scale: pointScale,
displacement: pointOffset
})
}))
} else {
styles.push(new style.Style({
image: new style.Circle({
radius: 10,
fill: new style.Fill({
color: '#e810dd',
}),
scale: pointScale,
displacement: pointOffset
})
}))
}
} else if (type === 'LineString') {
styles.push(new style.Style({
stroke: new style.Stroke({
color: lineColor,
width: lineWidth,
lineDash: lineDash ?? globalLineDash[Number(lineType)]
})
}))
} else if (type === 'Polygon') {
styles.push(new style.Style({
stroke: new style.Stroke({
color: polyBorderColor,
width: polyBorderWidth,
lineDash: polyBorderDash ?? globalLineDash[Number(polyBorderType)]
}),
fill: new style.Fill({
color: polyColor,
}),
}))
}
if (v.text) {
styles.push(new style.Style({
text: new style.Text({
font: "16px bold 微软雅黑",
text: v.text,
fill: new style.Fill({
color: '#ffffff'
}),
stroke: new style.Stroke({
color: '#D26CDB',
width: 2
}),
offsetY: textOffsetY,
}),
}))
}
}
feat.set('val', v)
feat.setStyle(styles)
return feat
})
_source.addFeatures(features)
}
let baseDrawTooltipElement;
let baseDrawHelpTooltipElement;
export const drawEdits = (map, obj, emitWkt) => {
return new Promise((resolve => {
if (!isValue(obj.textOffsetY)) {
obj.textOffsetY = (obj.featureType === 'Point' ? -30 : 0)
}
let commonStyle = (showText) => {
if (obj.featureType === 'Point') {
return new style.Style({
image: obj.pointIcon ? new style.Icon({
src: obj.pointIcon,
scale: obj.pointScale,
displacement: obj.pointOffset
}) : new style.Circle({
radius: 10,
fill: new style.Fill({
color: '#e810dd',
}),
scale: obj.pointScale,
displacement: obj.pointOffset
}),
text: (showText && obj.text) ? new style.Text({
font: "16px bold 微软雅黑",
text: obj.text,
fill: new style.Fill({
color: '#ffffff'
}),
stroke: new style.Stroke({
color: '#D26CDB',
width: 2
}),
offsetY: obj.textOffsetY,
}) : undefined,
})
} else if (obj.featureType === 'LineString') {
return new style.Style({
stroke: new style.Stroke({
color: obj.lineColor,
width: obj.lineWidth,
lineDash: obj.lineDash ?? globalLineDash[Number(obj.lineType)]
}),
text: (showText && obj.text) ? new style.Text({
font: "16px bold 微软雅黑",
text: obj.text,
fill: new style.Fill({
color: '#ffffff'
}),
stroke: new style.Stroke({
color: '#D26CDB',
width: 2
}),
offsetY: obj.textOffsetY,
}) : undefined,
})
} else if (obj.featureType === 'Polygon') {
return new style.Style({
stroke: new style.Stroke({
color: obj.polyBorderColor,
width: obj.polyBorderWidth,
lineDash: obj.polyBorderDash ?? globalLineDash[Number(obj.polyBorderType)]
}),
fill: new style.Fill({
color: obj.polyColor,
}),
text: (showText && obj.text) ? new style.Text({
font: "16px bold 微软雅黑",
text: obj.text,
fill: new style.Fill({
color: '#ffffff'
}),
stroke: new style.Stroke({
color: '#D26CDB',
width: 2
}),
offsetY: obj.textOffsetY,
}) : undefined,
})
}
}
const getWkt = (feature) => {
const gm = feature.getGeometry()
let wkt = ''
const gType = gm.getType()
if (gType === 'Polygon') {
wkt = formatPosition.cpnTwpn(gm.getCoordinates())
} else if (gType === 'LineString') {
wkt = formatPosition.clTwl(gm.getCoordinates())
} else if (gType === 'Circle') {
const circlePoly = fromCircle(gm, 128)
feature.setGeometry(circlePoly)
wkt = formatPosition.cpnTwpn(circlePoly.getCoordinates())
} else if (gType === 'Point') {
wkt = formatPosition.cptTwpt(gm.getCoordinates())
}
emitWkt(wkt)
}
const reset = () => {
const oldLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
if (oldLayer) {
map.removeLayer(oldLayer[0])
}
const oldDraw = map.getInteractions().getArray().filter(v => v.get(drawFlag[0]) === drawFlag[1])
if (oldDraw) {
map.removeInteraction(oldDraw[0])
}
const oldModify = map.getInteractions().getArray().filter(v => v.get(modifyFlag[0]) === modifyFlag[1])
if (oldModify) {
map.removeInteraction(oldModify[0])
}
}
if (!baseDrawTooltipElement) {
reset()
if (obj.wkt) {
drawViews(map, [obj])
}
let _source
const realLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
if (realLayer[0]) {
_source = realLayer[0].getSource()
} else {
_source = new source.Vector(); //图层数据源
const _vector = new layer.Vector({
zIndex: 9999,
source: _source,
style: commonStyle(true),
});
_vector.set(layerFlag[0], layerFlag[1])
map.addLayer(_vector);
}
const modifyInteraction = new Modify({
source: _source,
});
modifyInteraction.set(modifyFlag[0], modifyFlag[1])
map.addInteraction(modifyInteraction)
modifyInteraction.on('modifyend', evt => {
try {
const feat = evt.features.item(0)
getWkt(feat)
} catch {
}
})
if (!obj.wkt) {
let sketch;
let helpTooltip;
let measureTooltip;
let continueMsg = '双击结束标绘';
const geodesicCheckbox = true;//测地学方式对象
const createMeasureTooltip = () => {
const id = 'baseDrawTooltipElementId'
if (baseDrawTooltipElement) {
map.removeOverlay(map.getOverlayById(id))
baseDrawTooltipElement.parentNode.removeChild(baseDrawTooltipElement);
}
baseDrawTooltipElement = document.createElement('div');
baseDrawTooltipElement.className = 'tooltip tooltip-measure';
measureTooltip = new ol.Overlay({
id,
element: baseDrawTooltipElement,
offset: [0, -15],
positioning: 'bottom-center'
});
map.addOverlay(measureTooltip);
}
const createHelpTooltip = () => {
const id = 'baseDrawHelpTooltipElementId'
if (baseDrawHelpTooltipElement) {
map.removeOverlay(map.getOverlayById(id))
baseDrawHelpTooltipElement.parentNode.removeChild(baseDrawHelpTooltipElement);
}
baseDrawHelpTooltipElement = document.createElement('div');
baseDrawHelpTooltipElement.className = 'tooltip hidden';
helpTooltip = new ol.Overlay({
id,
element: baseDrawHelpTooltipElement,
offset: [15, 0],
positioning: 'center-left'
});
map.addOverlay(helpTooltip);
}
const formatLength = (line) => {
// 获取投影坐标系
const sourceProj = map.getView().getProjection();
// ol/sphere里有getLength()和getArea()用来测量距离和区域面积,默认的投影坐标系是EPSG:3857, 其中有个options的参数,可以设置投影坐标系
const length = sphere.getLength(line, {projection: sourceProj});
// const length = getLength(line);
let output;
if (length > 100) {
const km = Math.round((length / 1000) * 100) / 100;
output = `${km} 千米
${parseFloat(String(km * 0.53995)).toFixed(2)} 海里`;
} else {
output = `${Math.round(length * 100) / 100} m`;
}
return output;
};
//获取圆的面积
const getCircleArea = (circle, projection) => {
const P = 3.14
const radius = getCircleRadius(circle, projection)
return P * radius * radius
}
//获取圆的半径
const getCircleRadius = (circle, projection) => {
return circle.getRadius() * projection.getMetersPerUnit()
}
const formatArea = (polygon, type= 'polygon') => {
let area
const sourceProj = map.getView().getProjection();
// 获取投影坐标系
if (type === 'polygon') {
area = sphere.getArea(polygon, {
projection: sourceProj,
});
} else if (type === 'circle') {
area = getCircleArea(polygon, sourceProj)
}
let output;
if (area > 10000) {
const km = Math.round((area / 1000000) * 100) / 100;
output = `${km} 平方公里
${parseFloat(String(km * 0.38610)).toFixed(
2
)} 平方英里`;
} else {
output = `${Math.round(area * 100) / 100} ` + " m2";
}
return output;
};
const addInteraction = () => {
const id = 'baseDrawName'
const draw = new interaction.Draw({
source: _source,//测量绘制层数据源
type: obj.featureType, //几何图形类型
// geometryFunction: typeSelect === 'rectangle' ? createBox() : null,
style: commonStyle(obj.featureType === 'Point' ? true : false),
});
draw.set('showText', obj.featureType === 'Point' ? true : false)
draw.set(drawFlag[0], drawFlag[1])
draw.set(id, id)
createMeasureTooltip(); //创建测量工具提示框
createHelpTooltip(); //创建帮助提示框
map.addInteraction(draw);
let listener;
//绑定交互绘制工具开始绘制的事件
const drawstartHandle = (evt) => {
store.commit('gis/SET_IS_DRAWING', true)
sketch = evt.feature; //绘制的要素
let tooltipCoord = evt.coordinate;// 绘制的坐标
//绑定change事件,根据绘制几何类型得到测量长度值或面积值,并将其设置到测量工具提示框中显示
listener = sketch.getGeometry().on('change', function (evt) {
const geom = evt.target
let output;
if (geom.getType() === 'LineString') {
output = formatLength(geom);//长度值
tooltipCoord = geom.getLastCoordinate();//坐标
} else if (geom.getType() === 'Polygon') {
output = formatArea(geom);//面积值
tooltipCoord = geom.getInteriorPoint().getCoordinates();//坐标
} else if (geom.getType() === 'Circle') {
output = formatArea(geom, 'circle');//面积值
tooltipCoord = geom.getCenter()
}
baseDrawTooltipElement.innerHTML = output;//将测量值设置到测量工具提示框中显示
measureTooltip.setPosition(tooltipCoord);//设置测量工具提示框的显示位置
});
}
draw.on('drawstart', drawstartHandle);
//绑定交互绘制工具结束绘制的事件
const copy = (value) => {
const str = document.createElement('input')
str.setAttribute('value', value)
document.body.appendChild(str)
str.select()
document.execCommand('copy')
document.body.removeChild(str)
}
const drawendHandle = (evt) => {
setTimeout(() => {
store.commit('gis/SET_IS_DRAWING', false)
}, 300)
map.removeInteraction(map.getInteractions().getArray().filter(v => v.get(id) === id)[0]);
// 标绘的时候不需要最终结果dom
map.removeOverlay(map.getOverlayById('baseDrawHelpTooltipElementId'))
baseDrawTooltipElement.parentNode.removeChild(baseDrawTooltipElement);
sketch = null; //置空当前绘制的要素对象
baseDrawTooltipElement = null; //置空测量工具提示框对象
baseDrawHelpTooltipElement.parentNode.removeChild(baseDrawHelpTooltipElement);
baseDrawHelpTooltipElement = null; //置空测量工具提示框对象
unByKey(listener);
draw.un('drawstart', drawstartHandle);
draw.un('drawend', drawendHandle);
map.removeInteraction(map.getInteractions().getArray().filter(v => v.get(id) === id)[0]);
map.un('pointermove', pointerMoveHandler)
const baseDrawFeature = evt.feature
getWkt(baseDrawFeature)
}
draw.on('drawend', drawendHandle);
}
addInteraction(); //调用加载绘制交互控件方法,添加绘图进行测量
const pointerMoveHandler = (evt) => {
if (evt.dragging) {
return;
}
let helpMsg = '单击开始标绘';//当前默认提示信息
//判断绘制几何类型设置相应的帮助提示信息
if (sketch) {
const geom = sketch.getGeometry()
helpMsg = continueMsg;
// if (geom.getType() === 'Polygon') {
// helpMsg = continueMsg; //绘制多边形时提示相应内容
// } else if (geom.getType() === 'LineString') {
// helpMsg = continueMsg; //绘制线时提示相应内容
// }
}
baseDrawHelpTooltipElement.innerHTML = helpMsg; //将提示信息设置到对话框中显示
helpTooltip.setPosition(evt.coordinate);//设置帮助提示框的位置
baseDrawHelpTooltipElement.classList.remove('hidden');//移除帮助提示框的隐藏样式进行显示
};
map.on('pointermove', pointerMoveHandler); //地图容器绑定鼠标移动事件,动态显示帮助提示框内容
//地图绑定鼠标移出事件,鼠标移出时为帮助提示框设置隐藏样式
try {
map.getViewport().on('mouseout', () => {
baseDrawHelpTooltipElement.addClass('hidden');
});
} catch (e) {
}
}
}
resolve(() => {
const oldLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
if (oldLayer) {
oldLayer[0].setStyle(commonStyle(true))
}
// const oldDraw = map.getInteractions().getArray().filter(v => v.get(drawFlag[0]) === drawFlag[1])
// if (oldDraw) {
// oldDraw[0].setStyle(commonStyle(oldDraw[0].get('showText')))
// }
})
}))
}