measure.ts 13 KB

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