draw.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. import * as ol from 'ol'
  2. import * as style from 'ol/style'
  3. import * as layer from 'ol/layer'
  4. import * as source from 'ol/source'
  5. import * as geom from 'ol/geom'
  6. import {fromCircle} from 'ol/geom/Polygon';
  7. import * as proj from 'ol/proj'
  8. import * as interaction from 'ol/interaction'
  9. import * as coordinate from 'ol/coordinate'
  10. import * as control from 'ol/control'
  11. import * as sphere from 'ol/sphere'
  12. import { unByKey } from 'ol/Observable'
  13. import './dom.scss'
  14. import {createBox} from "ol/interaction/Draw";
  15. import {Circle, LineString, Polygon} from "ol/geom";
  16. import {formatPosition} from '@/utils/easyMap'
  17. import store from "@/store";
  18. const layerFlag = ['layerName', 'drawLayer']
  19. let drawTooltipElement;
  20. let drawHelpTooltipElement;
  21. const typeMapper = new Map([
  22. ['line', 'LineString'],
  23. ['rectangle', 'LineString'],
  24. ['polygon', 'Polygon'],
  25. ['circle', 'Circle'],
  26. ])
  27. let oldDrawFeature
  28. /**
  29. *
  30. * @param map
  31. * @param typeSelect line线,rectangle矩形,polygon多边形,circle圆形
  32. */
  33. export default function Draw (map, typeSelect) {
  34. return new Promise((resolve, reject) => {
  35. if (!drawTooltipElement) {
  36. let _source
  37. const realLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
  38. if (realLayer[0]) {
  39. _source = realLayer[0].getSource()
  40. } else {
  41. _source = new source.Vector(); //图层数据源
  42. const _vector = new layer.Vector({
  43. zIndex: 9999,
  44. source: _source,
  45. style: new style.Style({ //图层样式
  46. fill: new style.Fill({
  47. color: 'rgba(46,129,255,0.15)' //填充颜色
  48. }),
  49. stroke: new style.Stroke({
  50. color: '#2E81FF', //边框颜色
  51. width: 2, // 边框宽度
  52. lineDash: [10, 10]
  53. }),
  54. image: new style.Circle({
  55. radius: 7,
  56. fill: new style.Fill({
  57. color: '#ffcc33'
  58. })
  59. })
  60. })
  61. });
  62. _vector.set(layerFlag[0], layerFlag[1])
  63. map.addLayer(_vector);
  64. }
  65. let sketch;
  66. let helpTooltip;
  67. let measureTooltip;
  68. let continueMsg = '双击结束标绘';
  69. const geodesicCheckbox = true;//测地学方式对象
  70. const createMeasureTooltip = () => {
  71. const id = 'drawTooltipElementId'
  72. if (drawTooltipElement) {
  73. map.removeOverlay(map.getOverlayById(id))
  74. drawTooltipElement.parentNode.removeChild(drawTooltipElement);
  75. }
  76. drawTooltipElement = document.createElement('div');
  77. drawTooltipElement.className = 'tooltip tooltip-measure';
  78. measureTooltip = new ol.Overlay({
  79. id,
  80. element: drawTooltipElement,
  81. offset: [0, -15],
  82. positioning: 'bottom-center'
  83. });
  84. map.addOverlay(measureTooltip);
  85. }
  86. const createHelpTooltip = () => {
  87. const id = 'drawHelpTooltipElementId'
  88. if (drawHelpTooltipElement) {
  89. map.removeOverlay(map.getOverlayById(id))
  90. drawHelpTooltipElement.parentNode.removeChild(drawHelpTooltipElement);
  91. }
  92. drawHelpTooltipElement = document.createElement('div');
  93. drawHelpTooltipElement.className = 'tooltip hidden';
  94. helpTooltip = new ol.Overlay({
  95. id,
  96. element: drawHelpTooltipElement,
  97. offset: [15, 0],
  98. positioning: 'center-left'
  99. });
  100. map.addOverlay(helpTooltip);
  101. }
  102. const formatLength = (line) => {
  103. // 获取投影坐标系
  104. const sourceProj = map.getView().getProjection();
  105. // ol/sphere里有getLength()和getArea()用来测量距离和区域面积,默认的投影坐标系是EPSG:3857, 其中有个options的参数,可以设置投影坐标系
  106. const length = sphere.getLength(line, {projection: sourceProj});
  107. // const length = getLength(line);
  108. let output;
  109. if (length > 100) {
  110. const km = Math.round((length / 1000) * 100) / 100;
  111. output = `${km} 千米 <br>${parseFloat(String(km * 0.53995)).toFixed(2)} 海里`;
  112. } else {
  113. output = `${Math.round(length * 100) / 100} m`;
  114. }
  115. return output;
  116. };
  117. //获取圆的面积
  118. const getCircleArea = (circle, projection) => {
  119. const P = 3.14
  120. const radius = getCircleRadius(circle, projection)
  121. return P * radius * radius
  122. }
  123. //获取圆的半径
  124. const getCircleRadius = (circle, projection) => {
  125. return circle.getRadius() * projection.getMetersPerUnit()
  126. }
  127. const formatArea = (polygon, type= 'polygon') => {
  128. let area
  129. const sourceProj = map.getView().getProjection();
  130. // 获取投影坐标系
  131. if (type === 'polygon') {
  132. area = sphere.getArea(polygon, {
  133. projection: sourceProj,
  134. });
  135. } else if (type === 'circle') {
  136. area = getCircleArea(polygon, sourceProj)
  137. }
  138. let output;
  139. if (area > 10000) {
  140. const km = Math.round((area / 1000000) * 100) / 100;
  141. output = `${km} 平方公里<br>${parseFloat(String(km * 0.38610)).toFixed(
  142. 2
  143. )} 平方英里`;
  144. } else {
  145. output = `${Math.round(area * 100) / 100} ` + " m<sup>2</sup>";
  146. }
  147. return output;
  148. };
  149. const addInteraction = () => {
  150. const id = 'drawName'
  151. const draw = new interaction.Draw({
  152. source: _source,//测量绘制层数据源
  153. // @ts-ignore
  154. type: typeMapper.get(typeSelect), //几何图形类型
  155. // @ts-ignore
  156. geometryFunction: typeSelect === 'rectangle' ? createBox() : null,
  157. style: new style.Style({
  158. fill: new style.Fill({
  159. color: "rgba(46,129,255,0.15)",
  160. }),
  161. stroke: new style.Stroke({
  162. color: "#2E81FF",
  163. width: 2,
  164. lineDash: [10, 10]
  165. }),
  166. image: new style.Circle({
  167. radius: 5,
  168. stroke: new style.Stroke({
  169. color: "rgba(0, 0, 0, 0.7)",
  170. }),
  171. fill: new style.Fill({
  172. color: "rgba(255, 255, 255, 0.2)",
  173. }),
  174. }),
  175. }),
  176. });
  177. draw.set(id, id)
  178. createMeasureTooltip(); //创建测量工具提示框
  179. createHelpTooltip(); //创建帮助提示框
  180. map.addInteraction(draw);
  181. let listener;
  182. //绑定交互绘制工具开始绘制的事件
  183. const drawstartHandle = (evt) => {
  184. store.dispatch('gis/LOAD_IS_TOOLING', true)
  185. sketch = evt.feature; //绘制的要素
  186. let tooltipCoord = evt.coordinate;// 绘制的坐标
  187. //绑定change事件,根据绘制几何类型得到测量长度值或面积值,并将其设置到测量工具提示框中显示
  188. listener = sketch.getGeometry().on('change', function (evt) {
  189. const geom = evt.target
  190. let output;
  191. if (geom.getType() === 'LineString') {
  192. output = formatLength(geom);//长度值
  193. tooltipCoord = geom.getLastCoordinate();//坐标
  194. } else if (geom.getType() === 'Polygon') {
  195. output = formatArea(geom);//面积值
  196. tooltipCoord = geom.getInteriorPoint().getCoordinates();//坐标
  197. } else if (geom.getType() === 'Circle') {
  198. output = formatArea(geom, 'circle');//面积值
  199. tooltipCoord = geom.getCenter()
  200. }
  201. drawTooltipElement.innerHTML = output;//将测量值设置到测量工具提示框中显示
  202. measureTooltip.setPosition(tooltipCoord);//设置测量工具提示框的显示位置
  203. });
  204. }
  205. draw.on('drawstart', drawstartHandle);
  206. //绑定交互绘制工具结束绘制的事件
  207. const copy = (value) => {
  208. const str = document.createElement('input')
  209. str.setAttribute('value', value)
  210. document.body.appendChild(str)
  211. str.select()
  212. document.execCommand('copy')
  213. document.body.removeChild(str)
  214. }
  215. const drawendHandle = (evt) => {
  216. setTimeout(() => {
  217. store.dispatch('gis/LOAD_IS_TOOLING', false)
  218. }, 300)
  219. map.removeInteraction(map.getInteractions().getArray().filter(v => v.get(id) === id)[0]);
  220. // const del = document.createElement("div");
  221. // del.className = "lineDel";
  222. // drawTooltipElement.append(del);
  223. // del.onclick = () => {
  224. // _source.removeFeature(evt.feature)
  225. // const b = del.parentElement.parentElement
  226. // b.parentElement.removeChild(b);
  227. // const g = evt.feature.getGeometry()
  228. // if (g.getType() === 'LineString') {
  229. // const w = `LINESTRING(${g.getCoordinates().map(v => v[0] + ' ' + v[1]).join(',')})`
  230. // copy(w)
  231. // } else if (g.getType() === 'Polygon') {
  232. // const w = `POLYGON(${g.getCoordinates().map(v => '(' + v.map(c => c[0] + ' ' + c[1]) + ')').join(',')})`
  233. // copy(w)
  234. // }
  235. // };
  236. // drawTooltipElement.className = 'tooltip tooltip-static'; //设置测量提示框的样式
  237. // measureTooltip.setOffset([0, -7]);
  238. // 标绘的时候不需要最终结果dom
  239. map.removeOverlay(map.getOverlayById('drawHelpTooltipElementId'))
  240. drawTooltipElement.parentNode.removeChild(drawTooltipElement);
  241. sketch = null; //置空当前绘制的要素对象
  242. drawTooltipElement = null; //置空测量工具提示框对象
  243. drawHelpTooltipElement.parentNode.removeChild(drawHelpTooltipElement);
  244. drawHelpTooltipElement = null; //置空测量工具提示框对象
  245. unByKey(listener);
  246. draw.un('drawstart', drawstartHandle);
  247. draw.un('drawend', drawendHandle);
  248. map.removeInteraction(map.getInteractions().getArray().filter(v => v.get(id) === id)[0]);
  249. map.un('pointermove', pointerMoveHandler)
  250. if (oldDrawFeature) {
  251. _source.removeFeature(oldDrawFeature)
  252. }
  253. oldDrawFeature = evt.feature
  254. const gm = oldDrawFeature.getGeometry()
  255. let wkt = ''
  256. const gType = gm.getType()
  257. if (gType === 'Polygon') {
  258. wkt = formatPosition.cpnTwpn(gm.getCoordinates())
  259. } else if (gType === 'LineString') {
  260. wkt = formatPosition.clTwl(gm.getCoordinates())
  261. } else if (gType === 'Circle') {
  262. const circlePoly = fromCircle(gm, 128)
  263. oldDrawFeature.setGeometry(circlePoly)
  264. wkt = formatPosition.cpnTwpn(circlePoly.getCoordinates())
  265. }
  266. resolve({feature: oldDrawFeature, wkt: wkt})
  267. }
  268. draw.on('drawend', drawendHandle);
  269. }
  270. addInteraction(); //调用加载绘制交互控件方法,添加绘图进行测量
  271. const pointerMoveHandler = (evt) => {
  272. if (evt.dragging) {
  273. return;
  274. }
  275. let helpMsg = '单击开始标绘';//当前默认提示信息
  276. //判断绘制几何类型设置相应的帮助提示信息
  277. if (sketch) {
  278. const geom = sketch.getGeometry()
  279. helpMsg = continueMsg;
  280. // if (geom.getType() === 'Polygon') {
  281. // helpMsg = continueMsg; //绘制多边形时提示相应内容
  282. // } else if (geom.getType() === 'LineString') {
  283. // helpMsg = continueMsg; //绘制线时提示相应内容
  284. // }
  285. }
  286. drawHelpTooltipElement.innerHTML = helpMsg; //将提示信息设置到对话框中显示
  287. helpTooltip.setPosition(evt.coordinate);//设置帮助提示框的位置
  288. drawHelpTooltipElement.classList.remove('hidden');//移除帮助提示框的隐藏样式进行显示
  289. };
  290. map.on('pointermove', pointerMoveHandler); //地图容器绑定鼠标移动事件,动态显示帮助提示框内容
  291. //地图绑定鼠标移出事件,鼠标移出时为帮助提示框设置隐藏样式
  292. try {
  293. map.getViewport().on('mouseout', () => {
  294. drawHelpTooltipElement.addClass('hidden');
  295. });
  296. } catch (e) {
  297. }
  298. } else {
  299. reject('正在标绘中,请勿重复操作!')
  300. }
  301. })
  302. }
  303. export const DrawClear = (map) => {
  304. const realLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
  305. if (realLayer[0]) {
  306. realLayer[0].getSource().clear()
  307. oldDrawFeature = null
  308. }
  309. }