Newer
Older
urbanLifeline_YanAn / public / static / libs / mapbox / extend / MeatureTool.js
@zhangqy zhangqy on 3 Oct 52 KB first commit
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined'
  3. ? (module.exports = factory())
  4. : typeof define === 'function' && define.amd
  5. ? define(factory)
  6. : ((global = typeof globalThis !== 'undefined' ? globalThis : global || self),
  7. (global.mapboxgl1.MeatureTool = factory(global.mapboxgl1)));
  8. })(window, function (mapboxgl) {
  9. const css = `
  10. /* Override default control style */
  11. .mapbox-gl-draw_ctrl-bottom-left,
  12. .mapbox-gl-draw_ctrl-top-left {
  13. margin-left:0;
  14. border-radius:0 4px 4px 0;
  15. }
  16. .mapbox-gl-draw_ctrl-top-right,
  17. .mapbox-gl-draw_ctrl-bottom-right {
  18. margin-right:0;
  19. border-radius:4px 0 0 4px;
  20. }
  21.  
  22. .mapbox-gl-draw_ctrl-draw-btn {
  23. border-color:rgba(0,0,0,0.9);
  24. color:rgba(255,255,255,0.5);
  25. width:30px;
  26. height:30px;
  27. }
  28.  
  29. .mapbox-gl-draw_ctrl-draw-btn.active,
  30. .mapbox-gl-draw_ctrl-draw-btn.active:hover {
  31. background-color:rgb(0 0 0/5%);
  32. }
  33. .mapbox-gl-draw_ctrl-draw-btn {
  34. background-repeat: no-repeat;
  35. background-position: center;
  36. }
  37.  
  38. .mapbox-gl-draw_point {
  39. background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="m10 2c-3.3 0-6 2.7-6 6s6 9 6 9 6-5.7 6-9-2.7-6-6-6zm0 2c2.1 0 3.8 1.7 3.8 3.8 0 1.5-1.8 3.9-2.9 5.2h-1.7c-1.1-1.4-2.9-3.8-2.9-5.2-.1-2.1 1.6-3.8 3.7-3.8z"/>%3C/svg>');
  40. }
  41. .mapbox-gl-draw_polygon {
  42. background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="m15 12.3v-4.6c.6-.3 1-1 1-1.7 0-1.1-.9-2-2-2-.7 0-1.4.4-1.7 1h-4.6c-.3-.6-1-1-1.7-1-1.1 0-2 .9-2 2 0 .7.4 1.4 1 1.7v4.6c-.6.3-1 1-1 1.7 0 1.1.9 2 2 2 .7 0 1.4-.4 1.7-1h4.6c.3.6 1 1 1.7 1 1.1 0 2-.9 2-2 0-.7-.4-1.4-1-1.7zm-8-.3v-4l1-1h4l1 1v4l-1 1h-4z"/>%3C/svg>');
  43. }
  44. .mapbox-gl-draw_line {
  45. background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="m13.5 3.5c-1.4 0-2.5 1.1-2.5 2.5 0 .3 0 .6.2.9l-3.8 3.8c-.3-.1-.6-.2-.9-.2-1.4 0-2.5 1.1-2.5 2.5s1.1 2.5 2.5 2.5 2.5-1.1 2.5-2.5c0-.3 0-.6-.2-.9l3.8-3.8c.3.1.6.2.9.2 1.4 0 2.5-1.1 2.5-2.5s-1.1-2.5-2.5-2.5z"/>%3C/svg>');
  46. }
  47. .mapbox-gl-draw_trash {
  48. background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="M10,3.4 c-0.8,0-1.5,0.5-1.8,1.2H5l-1,1v1h12v-1l-1-1h-3.2C11.5,3.9,10.8,3.4,10,3.4z M5,8v7c0,1,1,2,2,2h6c1,0,2-1,2-2V8h-2v5.5h-1.5V8h-3 v5.5H7V8H5z"/>%3C/svg>');
  49. }
  50. .mapbox-gl-draw_uncombine {
  51. background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="m12 2c-.3 0-.5.1-.7.3l-1 1c-.4.4-.4 1 0 1.4l1 1c.4.4 1 .4 1.4 0l1-1c.4-.4.4-1 0-1.4l-1-1c-.2-.2-.4-.3-.7-.3zm4 4c-.3 0-.5.1-.7.3l-1 1c-.4.4-.4 1 0 1.4l1 1c.4.4 1 .4 1.4 0l1-1c.4-.4.4-1 0-1.4l-1-1c-.2-.2-.4-.3-.7-.3zm-7 1c-1 0-1 1-.5 1.5.3.3 1 1 1 1l-1 1s-.5.5 0 1 1 0 1 0l1-1 1 1c.5.5 1.5.5 1.5-.5v-4zm-5 3c-.3 0-.5.1-.7.3l-1 1c-.4.4-.4 1 0 1.4l4.9 4.9c.4.4 1 .4 1.4 0l1-1c.4-.4.4-1 0-1.4l-4.9-4.9c-.1-.2-.4-.3-.7-.3z"/>%3C/svg>');
  52. }
  53. .mapbox-gl-draw_combine {
  54. background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="M12.1,2c-0.3,0-0.5,0.1-0.7,0.3l-1,1c-0.4,0.4-0.4,1,0,1.4l4.9,4.9c0.4,0.4,1,0.4,1.4,0l1-1 c0.4-0.4,0.4-1,0-1.4l-4.9-4.9C12.6,2.1,12.3,2,12.1,2z M8,8C7,8,7,9,7.5,9.5c0.3,0.3,1,1,1,1l-1,1c0,0-0.5,0.5,0,1s1,0,1,0l1-1l1,1 C11,13,12,13,12,12V8H8z M4,10c-0.3,0-0.5,0.1-0.7,0.3l-1,1c-0.4,0.4-0.4,1,0,1.4l1,1c0.4,0.4,1,0.4,1.4,0l1-1c0.4-0.4,0.4-1,0-1.4 l-1-1C4.5,10.1,4.3,10,4,10z M8,14c-0.3,0-0.5,0.1-0.7,0.3l-1,1c-0.4,0.4-0.4,1,0,1.4l1,1c0.4,0.4,1,0.4,1.4,0l1-1 c0.4-0.4,0.4-1,0-1.4l-1-1C8.5,14.1,8.3,14,8,14z"/>%3C/svg>');
  55. }
  56.  
  57. .mapboxgl-map.mouse-pointer .mapboxgl-canvas-container.mapboxgl-interactive {
  58. cursor: pointer;
  59. }
  60. .mapboxgl-map.mouse-move .mapboxgl-canvas-container.mapboxgl-interactive {
  61. cursor: move;
  62. }
  63. .mapboxgl-map.mouse-add .mapboxgl-canvas-container.mapboxgl-interactive {
  64. cursor: crosshair;
  65. }
  66. .mapboxgl-map.mouse-move.mode-direct_select .mapboxgl-canvas-container.mapboxgl-interactive {
  67. cursor: grab;
  68. cursor: -moz-grab;
  69. cursor: -webkit-grab;
  70. }
  71. .mapboxgl-map.mode-direct_select.feature-vertex.mouse-move .mapboxgl-canvas-container.mapboxgl-interactive {
  72. cursor: move;
  73. }
  74. .mapboxgl-map.mode-direct_select.feature-midpoint.mouse-pointer .mapboxgl-canvas-container.mapboxgl-interactive {
  75. cursor: cell;
  76. }
  77. .mapboxgl-map.mode-direct_select.feature-feature.mouse-move .mapboxgl-canvas-container.mapboxgl-interactive {
  78. cursor: move;
  79. }
  80. .mapboxgl-map.mode-static.mouse-pointer .mapboxgl-canvas-container.mapboxgl-interactive {
  81. cursor: grab;
  82. cursor: -moz-grab;
  83. cursor: -webkit-grab;
  84. }
  85.  
  86. .mapbox-gl-draw_boxselect {
  87. pointer-events: none;
  88. position: absolute;
  89. top: 0;
  90. left: 0;
  91. width: 0;
  92. height: 0;
  93. background: rgba(0,0,0,.1);
  94. border: 2px dotted #fff;
  95. opacity: 0.5;
  96. }
  97. .measure-move {
  98. border-radius: 3px;
  99. height: 16px;
  100. line-height: 16px;
  101. padding: 0 3px;
  102. font-size: 12px;
  103. box-shadow: 0 0 0 1px #ccc;
  104. float: right;
  105. cursor: default;
  106. }
  107.  
  108. .measure-result {
  109. border-radius: 3px;
  110. height: 16px;
  111. line-height: 16px;
  112. padding: 0 3px;
  113. font-size: 12px;
  114. box-shadow: 0 0 0 1px #ccc;
  115. float: right;
  116. cursor: default;
  117. z-index: 10;
  118. }
  119.  
  120. .close {
  121. width: 3px;
  122. height: 14px;
  123. text-align: center;
  124. border-radius: 3px;
  125. padding-top: 0px;
  126. padding-right: 10px;
  127. box-shadow: 0 0 0 0px #ccc;
  128. cursor: pointer;
  129. background: url(../measureicon/close.png) no-repeat center;
  130. border: 1px solid rgba(100, 100, 100, 0)
  131. }
  132.  
  133. .clear {
  134. width: 3px;
  135. height: 14px;
  136. text-align: center;
  137. border-radius: 3px;
  138. padding-right: 10px;
  139. box-shadow: 0 0 0 0px #ccc;
  140. cursor: pointer;
  141. float: right;
  142. background: url(../measureicon/delete.png) no-repeat center;
  143. border: 1px solid rgba(100, 100, 100, 0)
  144. }
  145.  
  146. .edit {
  147. width: 3px;
  148. height: 14px;
  149. text-align: center;
  150. border-radius: 3px;
  151. padding-right: 10px;
  152. box-shadow: 0 0 0 0px #ccc;
  153. cursor: pointer;
  154. float: right;
  155. background: url(../measureicon/edit.png) no-repeat center;
  156. border: 1px solid rgba(100, 100, 100, 0)
  157. }
  158.  
  159. .close:hover {
  160. border: 1px solid rgba(52, 98, 152, 1);
  161. }
  162.  
  163. .clear:hover {
  164. border: 1px solid rgba(52, 98, 152, 1);
  165. }
  166.  
  167. .edit:hover {
  168. border: 1px solid rgba(52, 98, 152, 1);
  169. }
  170. `;
  171. document.write(`<style type='text/css'>${css}</style>`);
  172.  
  173. return class {
  174. constructor(map) {
  175. this.map = map;
  176. }
  177.  
  178. layerDistanceList = [];
  179. layerAreaList = [];
  180. layerPointList = [];
  181. setDistance = () => {};
  182. setArea = () => {};
  183.  
  184. /**
  185. * 测量距离
  186. * @param {*} layerId
  187. */
  188. measureDistance(layerId, isP = true, callback = () => {}) {
  189. this.layerDistanceList.push(layerId);
  190. var isMeasure = true;
  191. const map = this.map;
  192. map.doubleClickZoom.disable();
  193. let catchMark = null;
  194. let isEdit = false;
  195. let Geolist = [];
  196. let dragPointOrder = 0;
  197. let pointOnLine = [0, 0];
  198.  
  199. const jsonPoint = {
  200. type: 'FeatureCollection',
  201. features: [],
  202. };
  203. const jsonLine = {
  204. type: 'FeatureCollection',
  205. features: [],
  206. };
  207.  
  208. // 添加测量结果弹窗
  209. const ele = document.createElement('div');
  210. ele.setAttribute('class', 'measure-move');
  211. const option = {
  212. element: ele,
  213. anchor: 'left',
  214. offset: [8, 0],
  215. };
  216. const tooltip = new mapboxgl.Marker(option).setLngLat([0, 0]);
  217. if (isP) tooltip.addTo(map);
  218.  
  219. // 添加测量图层
  220. map.addSource('points' + layerId, {
  221. type: 'geojson',
  222. data: jsonPoint,
  223. });
  224. map.addSource('point-move' + layerId, {
  225. type: 'geojson',
  226. data: jsonPoint,
  227. });
  228. map.addSource('line' + layerId, {
  229. type: 'geojson',
  230. data: jsonLine,
  231. });
  232. map.addSource('line-move' + layerId, {
  233. type: 'geojson',
  234. data: jsonLine,
  235. });
  236. map.addSource('point-follow' + layerId, {
  237. type: 'geojson',
  238. data: jsonPoint,
  239. });
  240. map.addLayer({
  241. id: 'line' + layerId,
  242. type: 'line',
  243. source: 'line' + layerId,
  244. paint: {
  245. 'line-color': '#ff0000',
  246. 'line-width': 2,
  247. 'line-opacity': 0.65,
  248. },
  249. });
  250. map.addLayer({
  251. id: 'line-move' + layerId,
  252. type: 'line',
  253. source: 'line-move' + layerId,
  254. paint: {
  255. 'line-color': '#ff0000',
  256. 'line-width': 2,
  257. 'line-opacity': 0.65,
  258. 'line-dasharray': [5, 2],
  259. },
  260. });
  261. map.addLayer({
  262. id: 'points' + layerId,
  263. type: 'circle',
  264. source: 'points' + layerId,
  265. paint: {
  266. 'circle-color': '#ffffff',
  267. 'circle-radius': 3.5,
  268. 'circle-stroke-width': 1.5,
  269. 'circle-stroke-color': '#ff0000',
  270. },
  271. });
  272. map.addLayer({
  273. id: 'point-move' + layerId,
  274. type: 'circle',
  275. source: 'point-move' + layerId,
  276. paint: {
  277. 'circle-color': '#ffffff',
  278. 'circle-radius': 3.5,
  279. 'circle-stroke-width': 1.5,
  280. 'circle-stroke-color': '#ff0000',
  281. },
  282. });
  283. // 活动点可以选择用图层,也可以选择用Mark
  284. map.addLayer({
  285. id: 'point-follow' + layerId,
  286. type: 'circle',
  287. source: 'point-follow' + layerId,
  288. paint: {
  289. 'circle-color': '#199afc',
  290. 'circle-radius': 5.5,
  291. 'circle-stroke-width': 1.5,
  292. 'circle-stroke-color': '#ffffff',
  293. },
  294. });
  295.  
  296. // 清除面积测量
  297. this.setArea = () => {
  298. isMeasure = false;
  299. if (map.getLayer('point-move' + layerId)) map.removeLayer('point-move' + layerId);
  300. if (map.getLayer('line-move' + layerId)) map.removeLayer('line-move' + layerId);
  301.  
  302. return isMeasure;
  303. };
  304.  
  305. /**
  306. * 添加点
  307. * @param {*} _e
  308. */
  309. function addPointforJSON(_e) {
  310. if (isMeasure) {
  311. const point = {
  312. type: 'Feature',
  313. geometry: {
  314. type: 'Point',
  315. coordinates: [_e.lngLat.lng, _e.lngLat.lat],
  316. },
  317. properties: {
  318. id: String(new Date().getTime()),
  319. },
  320. };
  321. jsonPoint.features.push(point);
  322. map.getSource('points' + layerId).setData(jsonPoint);
  323. drawLine(jsonPoint);
  324. addMeasureRes(jsonPoint);
  325. }
  326. }
  327.  
  328. /**
  329. * 绘制线
  330. * @param {*} jsonPoint
  331. */
  332. function drawLine(jsonPoint) {
  333. if (jsonPoint.features.length > 1) {
  334. jsonLine.features = [];
  335. for (let i = 0; i < jsonPoint.features.length - 1; i++) {
  336. const coords = jsonPoint.features[i].geometry.coordinates;
  337. const next_coords = jsonPoint.features[i + 1].geometry.coordinates;
  338. jsonLine.features.push({
  339. type: 'Feature',
  340. geometry: {
  341. type: 'LineString',
  342. coordinates: [coords, next_coords],
  343. },
  344. });
  345. }
  346. map.getSource('line' + layerId).setData(jsonLine);
  347. }
  348. }
  349.  
  350. /**
  351. * 添加dom
  352. * @param {*} jsonPoint 点集
  353. */
  354. function addMeasureRes(jsonPoint) {
  355. if (isP && jsonPoint.features.length > 0) {
  356. removedom();
  357. const pointList = [];
  358. for (let i = 0; i < jsonPoint.features.length; i++) {
  359. const coords = jsonPoint.features[i].geometry.coordinates;
  360. pointList.push(coords);
  361.  
  362. const close = document.createElement('div');
  363. close.setAttribute('class', `measure-result ${layerId} close`);
  364. close.onclick = __e => {
  365. // 移除点
  366. __e.stopPropagation();
  367. removePoint(coords);
  368. map.off('mousemove', onMouseMove);
  369. map.off('mousedown', onmousedown);
  370. if (catchMark) {
  371. catchMark.remove();
  372. }
  373. };
  374.  
  375. const clear = document.createElement('div');
  376. clear.setAttribute('class', `measure-result ${layerId} clear`);
  377. clear.onclick = __e => {
  378. // 全部删除
  379. __e.stopPropagation();
  380. removeLayer();
  381. map.off('mousemove', onMouseMove);
  382. map.off('mousedown', onmousedown);
  383. if (catchMark) {
  384. catchMark.remove();
  385. }
  386. };
  387.  
  388. const edit = document.createElement('div');
  389. edit.setAttribute('class', `measure-result ${layerId} edit`);
  390. edit.onclick = __e => {
  391. // 编辑线
  392. __e.stopPropagation();
  393. map.off('mousemove', onMouseMove);
  394. map.off('mousedown', onmousedown);
  395. if (catchMark) {
  396. catchMark.remove();
  397. }
  398. editLine();
  399. };
  400.  
  401. const element = document.createElement('div');
  402. element.setAttribute('class', 'measure-result ' + layerId);
  403. const option = {
  404. element: element,
  405. anchor: 'left',
  406. offset: [8, 0],
  407. };
  408. element.innerHTML = i === 0 ? '起点' : getLength(pointList);
  409. if ((jsonPoint.features.length === i + 1) & !isMeasure) {
  410. element.appendChild(edit);
  411. element.appendChild(clear);
  412. }
  413. element.appendChild(close);
  414. new mapboxgl.Marker(option).setLngLat(coords).addTo(map);
  415. }
  416. }
  417. }
  418.  
  419. /**
  420. * 移除点
  421. * @param {*} coords 点坐标
  422. */
  423. function removePoint(coords) {
  424. if (jsonPoint.features.length > 0) {
  425. if (jsonPoint.features.length === 2) {
  426. jsonPoint.features = [];
  427. jsonLine.features = [];
  428. map.getSource('points' + layerId).setData(jsonPoint);
  429. map.getSource('line' + layerId).setData(jsonLine);
  430. removedom();
  431. } else {
  432. for (let i = 0; i < jsonPoint.features.length; i++) {
  433. if (
  434. (jsonPoint.features[i].geometry.coordinates[0] === coords[0]) &
  435. (jsonPoint.features[i].geometry.coordinates[1] === coords[1])
  436. ) {
  437. jsonPoint.features.splice(i, 1);
  438. }
  439. }
  440. drawLine(jsonPoint);
  441. addMeasureRes(jsonPoint);
  442. map.getSource('points' + layerId).setData(jsonPoint);
  443. }
  444. }
  445. }
  446.  
  447. /**
  448. * 计算长度
  449. * @param {*} pointList
  450. * @returns
  451. */
  452. function getLength(pointList) {
  453. const line = turf.lineString(pointList);
  454. let len = turf.length(line);
  455. if (len < 1) {
  456. len = Math.round(len * 1000) + '米';
  457. } else {
  458. len = len.toFixed(2) + '公里';
  459. }
  460. return len;
  461. }
  462.  
  463. /**
  464. * 移除dom
  465. */
  466. function removedom() {
  467. const dom = document.getElementsByClassName('measure-result ' + layerId);
  468. const len = dom.length;
  469. if (len) {
  470. for (let i = len - 1; i >= 0; i--) {
  471. if (dom[i]) dom[i].remove();
  472. }
  473. }
  474. }
  475.  
  476. /**
  477. * 移除图层
  478. */
  479. function removeLayer() {
  480. jsonPoint.features = [];
  481. jsonLine.features = [];
  482. if (map.getLayer('points' + layerId)) map.removeLayer('points' + layerId);
  483. if (map.getLayer('line' + layerId)) map.removeLayer('line' + layerId);
  484. removedom();
  485. }
  486.  
  487. /**
  488. * 鼠标move事件
  489. * @param {} _e
  490. */
  491. function mouseMove(_e) {
  492. if (isMeasure) {
  493. map.getCanvas().style.cursor = 'default';
  494. var coords = [_e.lngLat.lng, _e.lngLat.lat];
  495. const jsonp = {
  496. type: 'Feature',
  497. geometry: {
  498. type: 'Point',
  499. coordinates: coords,
  500. },
  501. };
  502. map.getSource('point-move' + layerId).setData(jsonp);
  503.  
  504. if (jsonPoint.features.length > 0) {
  505. const pointList = [];
  506. for (let i = 0; i < jsonPoint.features.length; i++) {
  507. const coord = jsonPoint.features[i].geometry.coordinates;
  508. pointList.push(coord);
  509. }
  510. pointList.push(coords);
  511. const prev = jsonPoint.features[jsonPoint.features.length - 1];
  512. const jsonl = {
  513. type: 'Feature',
  514. geometry: {
  515. type: 'LineString',
  516. coordinates: [prev.geometry.coordinates, coords],
  517. },
  518. };
  519. map.getSource('line-move' + layerId).setData(jsonl);
  520. ele.innerHTML = getLength(pointList);
  521. tooltip.setLngLat(coords);
  522. }
  523. }
  524. }
  525.  
  526. /**
  527. * 绘制完成
  528. * @param {*} _e
  529. */
  530. function finish(_e) {
  531. if (isMeasure) {
  532. isMeasure = false;
  533. var coords = [_e.lngLat.lng, _e.lngLat.lat];
  534. removePoint(coords);
  535. if (map.getLayer('point-move' + layerId)) map.removeLayer('point-move' + layerId);
  536. if (map.getLayer('line-move' + layerId)) map.removeLayer('line-move' + layerId);
  537. map.getCanvas().style.cursor = 'default';
  538. tooltip.remove();
  539. let features = map.getSource('line' + layerId)._data.features;
  540. let coordinates = features.map(i => turf.coordAll(i)[0]);
  541. if (coordinates.length == 1) coordinates = turf.coordAll(features[0]);
  542. callback(turf.lineString(coordinates));
  543. }
  544. }
  545.  
  546. map.on('click', function (_e) {
  547. addPointforJSON(_e);
  548. });
  549.  
  550. map.on('mousemove', function (_e) {
  551. mouseMove(_e);
  552. });
  553.  
  554. map.on('dblclick', function (_e) {
  555. finish(_e);
  556. });
  557.  
  558. /**
  559. * 编辑测量线
  560. */
  561. function editLine() {
  562. catchMark = createMarker();
  563. UpdataGeolist();
  564.  
  565. map.on('mousemove', onMouseMove);
  566. map.on('mousedown', onmousedown);
  567. }
  568.  
  569. function onMouseMove(e) {
  570. const moveCoord = [e.lngLat.lng, e.lngLat.lat];
  571.  
  572. if (jsonPoint.features.length > 1) {
  573. // 计算当前指针与线段最近的点
  574. pointOnLine = getNearestPointOnLine(Geolist, moveCoord); // 自己计算
  575. const screenOnLine = Object.values(map.project(pointOnLine)); // 线上屏幕坐标
  576. const screenP = [e.point.x, e.point.y];
  577. const screenDist = screenDistance(screenOnLine, screenP); // 距离
  578. if (screenDist < 15) {
  579. isEdit = true;
  580. catchMark.setLngLat(pointOnLine).addTo(map);
  581. catchMark.getElement().style.display = 'block';
  582. } else {
  583. isEdit = false;
  584. catchMark.getElement().style.display = 'none';
  585. }
  586. } else {
  587. isEdit = false;
  588. catchMark.getElement().style.display = 'none';
  589. map.dragPan.enable();
  590. }
  591. }
  592.  
  593. function onmousedown(e) {
  594. if (isEdit) {
  595. map.dragPan.disable();
  596. let isExist = false;
  597.  
  598. // 首先判断编辑点是否是存在(存在修改原来点,不存在新加点)
  599. for (let i = 0; i < jsonPoint.features.length; i++) {
  600. const coord = jsonPoint.features[i].geometry.coordinates;
  601. if (coord[0] === pointOnLine[0] && coord[1] === pointOnLine[1]) {
  602. isExist = true;
  603. }
  604. }
  605.  
  606. // 获取编辑点在列表中的位置
  607. dragPointOrder = getDragCoords(pointOnLine, Geolist);
  608.  
  609. if (!isExist) {
  610. // 添加编辑点
  611. const point = {
  612. type: 'Feature',
  613. geometry: {
  614. type: 'Point',
  615. coordinates: pointOnLine,
  616. },
  617. properties: {
  618. id: String(new Date().getTime()),
  619. },
  620. };
  621. jsonPoint.features.splice(dragPointOrder, 0, point);
  622.  
  623. // 更新绘制要素
  624. updataFeature();
  625. }
  626.  
  627. map.on('mousemove', onDrag);
  628. map.on('mouseup', onMouseup);
  629. }
  630. }
  631.  
  632. function onDrag(e) {
  633. // 开始计时
  634. // var start = new Date().getTime()
  635. const movePoint = [e.lngLat.lng, e.lngLat.lat];
  636.  
  637. // 点跟随鼠标移动
  638. jsonPoint.features[dragPointOrder].geometry.coordinates = movePoint;
  639.  
  640. // 更新绘制要素
  641. updataFeature();
  642.  
  643. // 计时结束
  644. // var end1 = new Date().getTime()
  645. // console.log('渲染时间:', end1 - start + 'ms')
  646. }
  647.  
  648. // 更新绘制要素
  649. function updataFeature() {
  650. UpdataGeolist();
  651. map.getSource('points' + layerId).setData(jsonPoint);
  652. drawLine(jsonPoint);
  653. addMeasureRes(jsonPoint);
  654. }
  655.  
  656. function onMouseup(e) {
  657. map.off('mousemove', onDrag);
  658. map.dragPan.enable();
  659. }
  660.  
  661. // 创建Marker
  662. function createMarker() {
  663. const markerParam = {
  664. map: this.map,
  665. lngLat: [0, 0],
  666. height: 13,
  667. width: 13,
  668. size: 13,
  669. isDrag: false,
  670. cursor: 'default',
  671. };
  672. return new mapboxgl.Marker().setLngLat(markerParam.lngLat).setDraggable(false).addTo(markerParam.map);
  673. }
  674.  
  675. // 更新点集
  676. function UpdataGeolist() {
  677. Geolist = [];
  678. for (let i = 0; i < jsonPoint.features.length; i++) {
  679. const coord = jsonPoint.features[i].geometry.coordinates;
  680. Geolist.push(coord);
  681. }
  682. }
  683.  
  684. // 计算点到直线最近距离的点
  685. function getNearestPointOnLine(list, moveCoord) {
  686. var dis, point1, point2;
  687. for (let i = 0; i < list.length - 1; i++) {
  688. const distance = getNearestDistance(moveCoord, list[i], list[i + 1]);
  689. if (i === 0) {
  690. dis = distance;
  691. point1 = list[i];
  692. point2 = list[i + 1];
  693. } else {
  694. if (distance < dis) {
  695. dis = distance;
  696. point1 = list[i];
  697. point2 = list[i + 1];
  698. }
  699. }
  700. }
  701. const Point = getNearestPoint(moveCoord, point1, point2);
  702. return Point;
  703. }
  704.  
  705. // 计算点point到线段point1, point2最近距离
  706. function getNearestDistance(point, point1, point2) {
  707. const P = {};
  708. const A = {};
  709. const B = {};
  710. P.x = point[0];
  711. P.y = point[1];
  712. A.x = point1[0];
  713. A.y = point1[1];
  714. B.x = point2[0];
  715. B.y = point2[1];
  716. // 计算向量AP和向量AB的点积
  717. const dotProduct = (P.x - A.x) * (B.x - A.x) + (P.y - A.y) * (B.y - A.y);
  718. // 计算向量AB的长度的平方
  719. const lengthSquare = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
  720. // 计算点P到线段AB的投影点C
  721. const t = dotProduct / lengthSquare;
  722. const C = { x: A.x + t * (B.x - A.x), y: A.y + t * (B.y - A.y) };
  723. // 如果点C在线段AB内,则点P到线段AB的最近距离为PC的长度;否则,点P到线段AB的最近距离为PA或PB中的较小值。
  724. const isInside = dotProduct >= 0 && dotProduct <= lengthSquare;
  725. if (isInside) {
  726. return Math.sqrt((P.x - C.x) * (P.x - C.x) + (P.y - C.y) * (P.y - C.y));
  727. } else {
  728. return Math.min(
  729. Math.sqrt((P.x - A.x) * (P.x - A.x) + (P.y - A.y) * (P.y - A.y)),
  730. Math.sqrt((P.x - B.x) * (P.x - B.x) + (P.y - B.y) * (P.y - B.y))
  731. );
  732. }
  733. }
  734.  
  735. // 计算点到直线最近的点 point点坐标,point1, point2直线两个端点
  736. function getNearestPoint(point, point1, point2) {
  737. var x, y, x0, y0, x1, y1, x2, y2;
  738. x0 = point[0];
  739. y0 = point[1];
  740. x1 = point1[0];
  741. y1 = point1[1];
  742. x2 = point2[0];
  743. y2 = point2[1];
  744.  
  745. if (x1 !== x2 && y1 !== y2) {
  746. const a = (y2 - y1) / (x2 - x1);
  747. const b = y1 - a * x1;
  748. const k2 = -1 / a;
  749. const b2 = y0 - k2 * x0;
  750. x = (b2 - b) / (a - k2);
  751. y = a * x + b;
  752. } else if (x1 === x2) {
  753. x = x1;
  754. y = y0;
  755. } else if (y1 === y2) {
  756. x = x0;
  757. y = y1;
  758. }
  759.  
  760. // 点不能超出线段
  761. if (x1 < x2) {
  762. if (x2 < x) {
  763. x = x2;
  764. } else if (x < x1) {
  765. x = x1;
  766. }
  767. } else {
  768. if (x1 < x) {
  769. x = x1;
  770. } else if (x < x2) {
  771. x = x2;
  772. }
  773. }
  774. if (y1 < y2) {
  775. if (y2 < y) {
  776. y = y2;
  777. } else if (y < y1) {
  778. y = y1;
  779. }
  780. } else {
  781. if (y1 < y) {
  782. y = y1;
  783. } else if (y < y2) {
  784. y = y2;
  785. }
  786. }
  787.  
  788. // 点吸附端点
  789. const screenX0 = Object.values(map.project([x0, y0])); // 屏幕坐标
  790. const screenX1 = Object.values(map.project([x1, y1])); // 屏幕坐标
  791. const screenX2 = Object.values(map.project([x2, y2])); // 屏幕坐标
  792. const screenDistX1 = screenDistance(screenX0, screenX1); // 距离
  793. const screenDistX2 = screenDistance(screenX0, screenX2); // 距离
  794. if (screenDistX1 < 10) {
  795. x = x1;
  796. y = y1;
  797. }
  798. if (screenDistX2 < 10) {
  799. x = x2;
  800. y = y2;
  801. }
  802.  
  803. return [x, y];
  804. }
  805.  
  806. // 屏幕距离
  807. function screenDistance(point1, point2) {
  808. const x2 = Math.pow(point1[0] - point2[0], 2);
  809. const y2 = Math.pow(point1[1] - point2[1], 2);
  810. const dist = Math.sqrt(x2 + y2);
  811.  
  812. return dist;
  813. }
  814.  
  815. // 计算编辑点在线段上的添加位置
  816. function getDragCoords(coords, list) {
  817. var x, y, x1, y1, x2, y2;
  818. let index = 0;
  819. x = coords[0];
  820. y = coords[1];
  821.  
  822. for (let i = 0; i < list.length - 1; i++) {
  823. x1 = list[i][0];
  824. y1 = list[i][1];
  825. x2 = list[i + 1][0];
  826. y2 = list[i + 1][1];
  827.  
  828. if (x === x1 && y === y1) {
  829. index = i;
  830. break;
  831. } else {
  832. // 计算线段的长度
  833. const length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
  834. // 计算点到线段起点的距离
  835. const distance1 = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2));
  836. // 计算点到线段终点的距离
  837. const distance2 = Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y - y2, 2));
  838. // 如果点到线段两个端点的距离之和等于线段的长度,则点在线段上
  839. if (Math.abs(length - distance1 - distance2) < 0.00001) {
  840. index = i + 1;
  841. break;
  842. }
  843. }
  844. }
  845. return index;
  846. }
  847. }
  848.  
  849. /**
  850. * 测量面积
  851. * @param {*} layerId
  852. */
  853. measureArea(layerId, isP = true, callback = () => {}) {
  854. this.layerAreaList.push(layerId);
  855. var isMeasure = true;
  856. const map = this.map;
  857. map.doubleClickZoom.disable();
  858. let catchMark = null;
  859. let isEdit = false;
  860. let Geolist = [];
  861. let dragPointOrder = 0;
  862. let pointOnLine = [0, 0];
  863.  
  864. const jsonPoint = {
  865. type: 'FeatureCollection',
  866. features: [],
  867. };
  868. const jsonLine = {
  869. type: 'FeatureCollection',
  870. features: [],
  871. };
  872.  
  873. const ele = document.createElement('div');
  874. ele.setAttribute('class', 'measure-move');
  875. const option = {
  876. element: ele,
  877. anchor: 'left',
  878. offset: [8, 0],
  879. };
  880. const tooltip = new mapboxgl.Marker(option).setLngLat([0, 0]);
  881. if (isP) tooltip.addTo(map);
  882. map.addSource('points-area' + layerId, {
  883. type: 'geojson',
  884. data: jsonPoint,
  885. });
  886. map.addSource('point-move' + layerId, {
  887. type: 'geojson',
  888. data: jsonPoint,
  889. });
  890. map.addSource('line-area' + layerId, {
  891. type: 'geojson',
  892. data: jsonLine,
  893. });
  894. map.addSource('line-move' + layerId, {
  895. type: 'geojson',
  896. data: jsonLine,
  897. });
  898. map.addLayer({
  899. id: 'line-move' + layerId,
  900. type: 'line',
  901. source: 'line-move' + layerId,
  902. paint: {
  903. 'line-color': '#ff0000',
  904. 'line-width': 2,
  905. 'line-opacity': 0.65,
  906. 'line-dasharray': [5, 2],
  907. },
  908. });
  909. map.addLayer({
  910. id: 'line-area' + layerId,
  911. type: 'fill',
  912. source: 'line-area' + layerId,
  913. paint: {
  914. 'fill-color': '#ff0000',
  915. 'fill-opacity': 0.1,
  916. },
  917. });
  918. map.addLayer({
  919. id: 'line-area-stroke' + layerId,
  920. type: 'line',
  921. source: 'line-area' + layerId,
  922. paint: {
  923. 'line-color': '#ff0000',
  924. 'line-width': 2,
  925. 'line-opacity': 0.65,
  926. },
  927. });
  928. map.addLayer({
  929. id: 'points-area' + layerId,
  930. type: 'circle',
  931. source: 'points-area' + layerId,
  932. paint: {
  933. 'circle-color': '#ffffff',
  934. 'circle-radius': 3.5,
  935. 'circle-stroke-width': 1.5,
  936. 'circle-stroke-color': '#ff0000',
  937. },
  938. });
  939. map.addLayer({
  940. id: 'point-move' + layerId,
  941. type: 'circle',
  942. source: 'point-move' + layerId,
  943. paint: {
  944. 'circle-color': '#ffffff',
  945. 'circle-radius': 3.5,
  946. 'circle-stroke-width': 1.5,
  947. 'circle-stroke-color': '#ff0000',
  948. },
  949. });
  950.  
  951. this.setDistance = () => {
  952. isMeasure = false;
  953. if (map.getLayer('point-move' + layerId)) map.removeLayer('point-move' + layerId);
  954. if (map.getLayer('line-move' + layerId)) map.removeLayer('line-move' + layerId);
  955.  
  956. return isMeasure;
  957. };
  958.  
  959. /**
  960. * 添加点
  961. * @param {*} _e
  962. */
  963. function addPointforJSON(_e) {
  964. if (isMeasure) {
  965. const point = {
  966. type: 'Feature',
  967. geometry: {
  968. type: 'Point',
  969. coordinates: [_e.lngLat.lng, _e.lngLat.lat],
  970. },
  971. properties: {
  972. id: String(new Date().getTime()),
  973. },
  974. };
  975. jsonPoint.features.push(point);
  976. map.getSource('points-area' + layerId).setData(jsonPoint);
  977. addMeasureRes(jsonPoint);
  978. }
  979. }
  980.  
  981. /**
  982. * 添加dom
  983. * @param {*} jsonPoint 点集
  984. */
  985. function addMeasureRes(jsonPoint) {
  986. if (isP && jsonPoint.features.length > 0) {
  987. removedom();
  988. const pointList = [];
  989. for (let i = 0; i < jsonPoint.features.length; i++) {
  990. const coords = jsonPoint.features[i].geometry.coordinates;
  991. pointList.push(coords);
  992.  
  993. const close = document.createElement('div');
  994. close.setAttribute('class', `measure-result ${layerId} close`);
  995. close.onclick = __e => {
  996. // 移除点
  997. __e.stopPropagation();
  998. removePoint(coords);
  999. map.off('mousemove', onMouseMove);
  1000. map.off('mousedown', onmousedown);
  1001. if (catchMark) {
  1002. catchMark.remove();
  1003. }
  1004. };
  1005. if ((jsonPoint.features.length === i + 1) & !isMeasure) {
  1006. const clear = document.createElement('div');
  1007. clear.setAttribute('class', `measure-result ${layerId} clear`);
  1008. clear.onclick = __e => {
  1009. // 全部移除
  1010. __e.stopPropagation();
  1011. removeLayer();
  1012. map.off('mousemove', onMouseMove);
  1013. map.off('mousedown', onmousedown);
  1014. if (catchMark) {
  1015. catchMark.remove();
  1016. }
  1017. };
  1018.  
  1019. const edit = document.createElement('div');
  1020. edit.setAttribute('class', `measure-result ${layerId} edit`);
  1021. edit.onclick = __e => {
  1022. // 编辑
  1023. __e.stopPropagation();
  1024. map.off('mousemove', onMouseMove);
  1025. map.off('mousedown', onmousedown);
  1026. if (catchMark) {
  1027. catchMark.remove();
  1028. }
  1029. editArea();
  1030. };
  1031.  
  1032. const element = document.createElement('div');
  1033. element.setAttribute('class', 'measure-result ' + layerId);
  1034. const option = {
  1035. element: element,
  1036. anchor: 'left',
  1037. offset: [0, 0],
  1038. };
  1039. element.innerHTML = getArea(pointList);
  1040. element.appendChild(edit);
  1041. element.appendChild(clear);
  1042. element.appendChild(close);
  1043. new mapboxgl.Marker(option).setLngLat(coords).addTo(map);
  1044. } else {
  1045. const option = {
  1046. element: close,
  1047. anchor: 'left',
  1048. offset: [5, -15],
  1049. };
  1050. new mapboxgl.Marker(option).setLngLat(coords).addTo(map);
  1051. }
  1052. }
  1053. }
  1054. }
  1055.  
  1056. /**
  1057. * 计算面积
  1058. * @param {*} pointList
  1059. * @returns
  1060. */
  1061. function getArea(pointList) {
  1062. pointList.push(pointList[0]);
  1063. const polygon = turf.polygon([pointList]);
  1064. let area = turf.area(polygon);
  1065. if (area < 10000) {
  1066. area = Math.round(area) + '平方米';
  1067. } else {
  1068. area = (area / 1000000).toFixed(5) + '平方公里';
  1069. }
  1070. return area;
  1071. }
  1072.  
  1073. /**
  1074. * 移除点
  1075. * @param {*} coords 点坐标
  1076. */
  1077. function removePoint(coords) {
  1078. if (jsonPoint.features.length > 0) {
  1079. if (jsonPoint.features.length === 3) {
  1080. jsonPoint.features = [];
  1081. jsonLine.features = [];
  1082. map.getSource('points-area' + layerId).setData(jsonPoint);
  1083. map.getSource('line-area' + layerId).setData(jsonLine);
  1084. removedom();
  1085. } else {
  1086. for (let i = 0; i < jsonPoint.features.length; i++) {
  1087. if (
  1088. (jsonPoint.features[i].geometry.coordinates[0] === coords[0]) &
  1089. (jsonPoint.features[i].geometry.coordinates[1] === coords[1])
  1090. ) {
  1091. jsonPoint.features.splice(i, 1);
  1092. }
  1093. }
  1094. addMeasureRes(jsonPoint);
  1095. map.getSource('points-area' + layerId).setData(jsonPoint);
  1096.  
  1097. const pointList = [];
  1098. for (let i = 0; i < jsonPoint.features.length; i++) {
  1099. const coord = jsonPoint.features[i].geometry.coordinates;
  1100. pointList.push(coord);
  1101. }
  1102. const pts = pointList.concat([pointList[0]]);
  1103. const jsona = {
  1104. type: 'Feature',
  1105. geometry: {
  1106. type: 'Polygon',
  1107. coordinates: [pts],
  1108. },
  1109. };
  1110. map.getSource('line-area' + layerId).setData(jsona);
  1111. }
  1112. }
  1113. }
  1114.  
  1115. /**
  1116. * 移除dom
  1117. */
  1118. function removedom() {
  1119. const dom = document.getElementsByClassName('measure-result ' + layerId);
  1120. const len = dom.length;
  1121. if (len) {
  1122. for (let i = len - 1; i >= 0; i--) {
  1123. if (dom[i]) dom[i].remove();
  1124. }
  1125. }
  1126. }
  1127.  
  1128. /**
  1129. * 移除图层
  1130. */
  1131. function removeLayer() {
  1132. jsonPoint.features = [];
  1133. jsonLine.features = [];
  1134. if (map.getLayer('points-area' + layerId)) map.removeLayer('points-area' + layerId);
  1135. if (map.getLayer('line-area' + layerId)) map.removeLayer('line-area' + layerId);
  1136. if (map.getLayer('line-area-stroke' + layerId)) map.removeLayer('line-area-stroke' + layerId);
  1137. removedom();
  1138. }
  1139.  
  1140. /**
  1141. * 鼠标move事件
  1142. * @param {} _e
  1143. */
  1144. function mouseMove(_e) {
  1145. if (isMeasure) {
  1146. map.getCanvas().style.cursor = 'default';
  1147. const coords = [_e.lngLat.lng, _e.lngLat.lat];
  1148. const jsonp = {
  1149. type: 'Feature',
  1150. geometry: {
  1151. type: 'Point',
  1152. coordinates: coords,
  1153. },
  1154. };
  1155. map.getSource('point-move' + layerId).setData(jsonp);
  1156.  
  1157. if (jsonPoint.features.length > 0) {
  1158. if (jsonPoint.features.length === 1) {
  1159. const prev = jsonPoint.features[jsonPoint.features.length - 1];
  1160. const jsonl = {
  1161. type: 'Feature',
  1162. geometry: {
  1163. type: 'LineString',
  1164. coordinates: [prev.geometry.coordinates, coords],
  1165. },
  1166. };
  1167. map.getSource('line-move' + layerId).setData(jsonl);
  1168. } else {
  1169. const json = {
  1170. type: 'FeatureCollection',
  1171. features: [],
  1172. };
  1173. map.getSource('line-move' + layerId).setData(json);
  1174.  
  1175. const pointList = [];
  1176. for (let i = 0; i < jsonPoint.features.length; i++) {
  1177. const coord = jsonPoint.features[i].geometry.coordinates;
  1178. pointList.push(coord);
  1179. }
  1180. pointList.push(coords);
  1181. const pts = pointList.concat([pointList[0]]);
  1182. const jsona = {
  1183. type: 'Feature',
  1184. geometry: {
  1185. type: 'Polygon',
  1186. coordinates: [pts],
  1187. },
  1188. };
  1189. map.getSource('line-area' + layerId).setData(jsona);
  1190. ele.innerHTML = getArea(pointList);
  1191. tooltip.setLngLat(coords);
  1192. }
  1193. }
  1194. }
  1195. }
  1196.  
  1197. /**
  1198. * 绘制完成
  1199. * @param {*} _e
  1200. */
  1201. function finish(_e) {
  1202. if (isMeasure) {
  1203. isMeasure = false;
  1204. const coords = [_e.lngLat.lng, _e.lngLat.lat];
  1205. removePoint(coords);
  1206. if (map.getLayer('point-move' + layerId)) map.removeLayer('point-move' + layerId);
  1207. if (map.getLayer('line-move' + layerId)) map.removeLayer('line-move' + layerId);
  1208. map.getCanvas().style.cursor = 'default';
  1209. tooltip.remove();
  1210. callback(map.getSource('line-area' + layerId)._data);
  1211. }
  1212. }
  1213.  
  1214. map.on('click', function (_e) {
  1215. addPointforJSON(_e);
  1216. });
  1217.  
  1218. map.on('dblclick', function (_e) {
  1219. finish(_e);
  1220. });
  1221.  
  1222. map.on('mousemove', function (_e) {
  1223. mouseMove(_e);
  1224. });
  1225.  
  1226. /**
  1227. * 编辑测量面
  1228. */
  1229. function editArea() {
  1230. catchMark = createMarker();
  1231. UpdataGeolist();
  1232.  
  1233. map.on('mousemove', onMouseMove);
  1234. map.on('mousedown', onmousedown);
  1235. }
  1236.  
  1237. function onMouseMove(e) {
  1238. const moveCoord = [e.lngLat.lng, e.lngLat.lat];
  1239.  
  1240. if (jsonPoint.features.length > 1) {
  1241. // 计算当前指针与线段的距离
  1242. pointOnLine = getNearestPointOnLine(Geolist, moveCoord); // 线上实际坐标
  1243. const screenOnLine = Object.values(map.project(pointOnLine)); // 线上屏幕坐标
  1244. const screenP = [e.point.x, e.point.y];
  1245. const screenDist = screenDistance(screenOnLine, screenP); // 距离
  1246. if (screenDist < 15) {
  1247. isEdit = true;
  1248. catchMark.setLngLat(pointOnLine).addTo(map);
  1249. catchMark.getElement().style.display = 'block';
  1250. } else {
  1251. isEdit = false;
  1252. catchMark.getElement().style.display = 'none';
  1253. }
  1254. } else {
  1255. isEdit = false;
  1256. catchMark.getElement().style.display = 'none';
  1257. map.dragPan.enable();
  1258. }
  1259. }
  1260.  
  1261. function onmousedown(e) {
  1262. if (isEdit) {
  1263. map.dragPan.disable();
  1264. let isExist = false;
  1265.  
  1266. // 首先判断编辑点是否是存在(存在修改原来点,不存在新加点)
  1267. for (let i = 0; i < jsonPoint.features.length; i++) {
  1268. const coord = jsonPoint.features[i].geometry.coordinates;
  1269. if (coord[0] === pointOnLine[0] && coord[1] === pointOnLine[1]) {
  1270. isExist = true;
  1271. }
  1272. }
  1273.  
  1274. // 获取编辑点在列表中的位置
  1275. dragPointOrder = getDragCoords(pointOnLine, Geolist);
  1276.  
  1277. if (!isExist) {
  1278. // 添加编辑点
  1279. const point = {
  1280. type: 'Feature',
  1281. geometry: {
  1282. type: 'Point',
  1283. coordinates: pointOnLine,
  1284. },
  1285. properties: {
  1286. id: String(new Date().getTime()),
  1287. },
  1288. };
  1289. jsonPoint.features.splice(dragPointOrder, 0, point);
  1290.  
  1291. // 更新绘制要素
  1292. updataFeature();
  1293. }
  1294.  
  1295. map.on('mousemove', onDrag);
  1296. map.on('mouseup', onMouseup);
  1297. }
  1298. }
  1299.  
  1300. function onDrag(e) {
  1301. const movePoint = [e.lngLat.lng, e.lngLat.lat];
  1302.  
  1303. // 点跟随鼠标移动
  1304. jsonPoint.features[dragPointOrder].geometry.coordinates = movePoint;
  1305.  
  1306. // 更新绘制要素
  1307. updataFeature();
  1308. }
  1309.  
  1310. // 更新绘制要素
  1311. function updataFeature() {
  1312. UpdataGeolist();
  1313. const pts = Geolist;
  1314. const jsona = {
  1315. type: 'Feature',
  1316. geometry: {
  1317. type: 'Polygon',
  1318. coordinates: [pts],
  1319. },
  1320. };
  1321. map.getSource('line-area' + layerId).setData(jsona);
  1322. map.getSource('points-area' + layerId).setData(jsonPoint);
  1323. addMeasureRes(jsonPoint);
  1324. }
  1325.  
  1326. function onMouseup(e) {
  1327. map.off('mousemove', onDrag);
  1328. map.dragPan.enable();
  1329. }
  1330.  
  1331. // 创建Marker
  1332. function createMarker() {
  1333. const markerParam = {
  1334. map: this.map,
  1335. lngLat: [0, 0],
  1336. height: 13,
  1337. width: 13,
  1338. size: 13,
  1339. isDrag: false,
  1340. cursor: 'default',
  1341. };
  1342. return new mapboxgl.Marker().setLngLat(markerParam.lngLat).setDraggable(false).addTo(markerParam.map);
  1343. }
  1344.  
  1345. // 更新点集
  1346. function UpdataGeolist() {
  1347. Geolist = [];
  1348. for (let i = 0; i < jsonPoint.features.length; i++) {
  1349. const coord = jsonPoint.features[i].geometry.coordinates;
  1350. Geolist.push(coord);
  1351. }
  1352. Geolist.push(Geolist[0]);
  1353. }
  1354.  
  1355. // 计算点到直线最近距离的点
  1356. function getNearestPointOnLine(list, moveCoord) {
  1357. var dis, point1, point2;
  1358. for (let i = 0; i < list.length - 1; i++) {
  1359. const distance = getNearestDistance(moveCoord, list[i], list[i + 1]);
  1360. if (i === 0) {
  1361. dis = distance;
  1362. point1 = list[i];
  1363. point2 = list[i + 1];
  1364. } else {
  1365. if (distance < dis) {
  1366. dis = distance;
  1367. point1 = list[i];
  1368. point2 = list[i + 1];
  1369. }
  1370. }
  1371. }
  1372. const Point = getNearestPoint(moveCoord, point1, point2);
  1373. return Point;
  1374. }
  1375.  
  1376. // 计算点point到线段point1, point2最近距离
  1377. function getNearestDistance(point, point1, point2) {
  1378. const P = {};
  1379. const A = {};
  1380. const B = {};
  1381. P.x = point[0];
  1382. P.y = point[1];
  1383. A.x = point1[0];
  1384. A.y = point1[1];
  1385. B.x = point2[0];
  1386. B.y = point2[1];
  1387. // 计算向量AP和向量AB的点积
  1388. const dotProduct = (P.x - A.x) * (B.x - A.x) + (P.y - A.y) * (B.y - A.y);
  1389. // 计算向量AB的长度的平方
  1390. const lengthSquare = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
  1391. // 计算点P到线段AB的投影点C
  1392. const t = dotProduct / lengthSquare;
  1393. const C = { x: A.x + t * (B.x - A.x), y: A.y + t * (B.y - A.y) };
  1394. // 如果点C在线段AB内,则点P到线段AB的最近距离为PC的长度;否则,点P到线段AB的最近距离为PA或PB中的较小值。
  1395. const isInside = dotProduct >= 0 && dotProduct <= lengthSquare;
  1396. if (isInside) {
  1397. return Math.sqrt((P.x - C.x) * (P.x - C.x) + (P.y - C.y) * (P.y - C.y));
  1398. } else {
  1399. return Math.min(
  1400. Math.sqrt((P.x - A.x) * (P.x - A.x) + (P.y - A.y) * (P.y - A.y)),
  1401. Math.sqrt((P.x - B.x) * (P.x - B.x) + (P.y - B.y) * (P.y - B.y))
  1402. );
  1403. }
  1404. }
  1405.  
  1406. // 计算点到直线最近的点 point点坐标,point1, point2直线两个端点
  1407. function getNearestPoint(point, point1, point2) {
  1408. var x, y, x0, y0, x1, y1, x2, y2;
  1409. x0 = point[0];
  1410. y0 = point[1];
  1411. x1 = point1[0];
  1412. y1 = point1[1];
  1413. x2 = point2[0];
  1414. y2 = point2[1];
  1415.  
  1416. if (x1 !== x2 && y1 !== y2) {
  1417. const a = (y2 - y1) / (x2 - x1);
  1418. const b = y1 - a * x1;
  1419. const k2 = -1 / a;
  1420. const b2 = y0 - k2 * x0;
  1421. x = (b2 - b) / (a - k2);
  1422. y = a * x + b;
  1423. } else if (x1 === x2) {
  1424. x = x1;
  1425. y = y0;
  1426. } else if (y1 === y2) {
  1427. x = x0;
  1428. y = y1;
  1429. }
  1430.  
  1431. // 点不能超出线段
  1432. if (x1 < x2) {
  1433. if (x2 < x) {
  1434. x = x2;
  1435. } else if (x < x1) {
  1436. x = x1;
  1437. }
  1438. } else {
  1439. if (x1 < x) {
  1440. x = x1;
  1441. } else if (x < x2) {
  1442. x = x2;
  1443. }
  1444. }
  1445. if (y1 < y2) {
  1446. if (y2 < y) {
  1447. y = y2;
  1448. } else if (y < y1) {
  1449. y = y1;
  1450. }
  1451. } else {
  1452. if (y1 < y) {
  1453. y = y1;
  1454. } else if (y < y2) {
  1455. y = y2;
  1456. }
  1457. }
  1458.  
  1459. // 点吸附端点
  1460. const screenX0 = Object.values(map.project([x0, y0])); // 屏幕坐标
  1461. const screenX1 = Object.values(map.project([x1, y1])); // 屏幕坐标
  1462. const screenX2 = Object.values(map.project([x2, y2])); // 屏幕坐标
  1463. const screenDistX1 = screenDistance(screenX0, screenX1); // 距离
  1464. const screenDistX2 = screenDistance(screenX0, screenX2); // 距离
  1465. if (screenDistX1 < 10) {
  1466. x = x1;
  1467. y = y1;
  1468. }
  1469. if (screenDistX2 < 10) {
  1470. x = x2;
  1471. y = y2;
  1472. }
  1473.  
  1474. return [x, y];
  1475. }
  1476.  
  1477. // 屏幕距离
  1478. function screenDistance(point1, point2) {
  1479. const x2 = Math.pow(point1[0] - point2[0], 2);
  1480. const y2 = Math.pow(point1[1] - point2[1], 2);
  1481. const dist = Math.sqrt(x2 + y2);
  1482.  
  1483. return dist;
  1484. }
  1485.  
  1486. // 计算编辑点在线段上的添加位置
  1487. function getDragCoords(coords, list) {
  1488. var x, y, x1, y1, x2, y2;
  1489. let index = 0;
  1490. x = coords[0];
  1491. y = coords[1];
  1492.  
  1493. for (let i = 0; i < list.length - 1; i++) {
  1494. x1 = list[i][0];
  1495. y1 = list[i][1];
  1496. x2 = list[i + 1][0];
  1497. y2 = list[i + 1][1];
  1498.  
  1499. if (x === x1 && y === y1) {
  1500. index = i;
  1501. break;
  1502. } else {
  1503. // 计算线段的长度
  1504. const length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
  1505. // 计算点到线段起点的距离
  1506. const distance1 = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2));
  1507. // 计算点到线段终点的距离
  1508. const distance2 = Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y - y2, 2));
  1509. // 如果点到线段两个端点的距离之和等于线段的长度,则点在线段上
  1510. if (Math.abs(length - distance1 - distance2) < 0.00001) {
  1511. index = i + 1;
  1512. break;
  1513. }
  1514. }
  1515. }
  1516. return index;
  1517. }
  1518. }
  1519.  
  1520. drawPoint(layerId, callback = () => {}) {
  1521. this.layerPointList.push(layerId);
  1522. const map = this.map;
  1523. map.doubleClickZoom.disable();
  1524. const jsonPoint = {
  1525. type: 'FeatureCollection',
  1526. features: [],
  1527. };
  1528. map.addSource('points' + layerId, {
  1529. type: 'geojson',
  1530. data: jsonPoint,
  1531. });
  1532. map.addLayer({
  1533. id: 'points' + layerId,
  1534. type: 'circle',
  1535. source: 'points' + layerId,
  1536. paint: {
  1537. 'circle-color': '#ffffff',
  1538. 'circle-radius': 3.5,
  1539. 'circle-stroke-width': 1.5,
  1540. 'circle-stroke-color': '#ff0000',
  1541. },
  1542. });
  1543.  
  1544. /**
  1545. * 添加点
  1546. * @param {*} _e
  1547. */
  1548. function addPointforJSON(_e) {
  1549. const point = {
  1550. type: 'Feature',
  1551. geometry: {
  1552. type: 'Point',
  1553. coordinates: [_e.lngLat.lng, _e.lngLat.lat],
  1554. },
  1555. properties: {
  1556. id: String(new Date().getTime()),
  1557. },
  1558. };
  1559. jsonPoint.features.push(point);
  1560. map.getSource('points' + layerId).setData(jsonPoint);
  1561. map.off('click', addPointforJSON);
  1562. callback(point);
  1563. }
  1564. map.on('click', addPointforJSON);
  1565. }
  1566.  
  1567. drawRectangle(layerId, callback = () => {}) {
  1568. this.layerPointList.push(layerId);
  1569. const map = this.map;
  1570. map.doubleClickZoom.disable();
  1571. const jsonPoint = {
  1572. type: 'FeatureCollection',
  1573. features: [],
  1574. };
  1575. const jsonArea = {
  1576. type: 'FeatureCollection',
  1577. features: [],
  1578. };
  1579. map.addSource('points' + layerId, {
  1580. type: 'geojson',
  1581. data: jsonPoint,
  1582. });
  1583. map.addSource('line-area' + layerId, {
  1584. type: 'geojson',
  1585. data: jsonArea,
  1586. });
  1587. map.addLayer({
  1588. id: 'line-area' + layerId,
  1589. type: 'fill',
  1590. source: 'line-area' + layerId,
  1591. paint: {
  1592. 'fill-color': '#ff0000',
  1593. 'fill-opacity': 0.1,
  1594. },
  1595. });
  1596. map.addLayer({
  1597. id: 'points' + layerId,
  1598. type: 'circle',
  1599. source: 'points' + layerId,
  1600. paint: {
  1601. 'circle-color': '#ffffff',
  1602. 'circle-radius': 3.5,
  1603. 'circle-stroke-width': 1.5,
  1604. 'circle-stroke-color': '#ff0000',
  1605. },
  1606. });
  1607.  
  1608. /**
  1609. * 添加点
  1610. * @param {*} _e
  1611. */
  1612. function addPointforJSON(_e) {
  1613. const point = {
  1614. type: 'Feature',
  1615. geometry: {
  1616. type: 'Point',
  1617. coordinates: [_e.lngLat.lng, _e.lngLat.lat],
  1618. },
  1619. properties: {
  1620. id: String(new Date().getTime()),
  1621. },
  1622. };
  1623. jsonPoint.features.push(point);
  1624. if (jsonPoint.features.length == 2) {
  1625. map.off('click', addPointforJSON);
  1626. map.off('mousemove', mouseRectangle);
  1627. callback(jsonArea.features[0]);
  1628. }
  1629. map.getSource('points' + layerId).setData(jsonPoint);
  1630. }
  1631.  
  1632. function mouseRectangle(_e) {
  1633. if (!jsonPoint.features.length) {
  1634. const point = {
  1635. type: 'Feature',
  1636. geometry: {
  1637. type: 'Point',
  1638. coordinates: [_e.lngLat.lng, _e.lngLat.lat],
  1639. },
  1640. properties: {
  1641. id: String(new Date().getTime()),
  1642. },
  1643. };
  1644. return map.getSource('points' + layerId).setData({ type: 'FeatureCollection', features: [point] });
  1645. }
  1646. let coordinates = jsonPoint.features[0].geometry.coordinates;
  1647. let bbox = coordinates.concat([_e.lngLat.lng, _e.lngLat.lat]).sort((a, b) => a - b);
  1648. let polygon = turf.bboxPolygon([bbox[2], bbox[0], bbox[3], bbox[1]]);
  1649. jsonArea.features = [polygon];
  1650. map.getSource('line-area' + layerId).setData(jsonArea);
  1651. }
  1652.  
  1653. map.on('click', addPointforJSON);
  1654.  
  1655. map.on('mousemove', mouseRectangle);
  1656. }
  1657.  
  1658. /**
  1659. * 清除所有测量要素
  1660. */
  1661. clearMeasureAll() {
  1662. const dom = document.getElementsByClassName('measure-result');
  1663. const len = dom.length;
  1664. if (len) {
  1665. for (let i = len - 1; i >= 0; i--) {
  1666. if (dom[i]) dom[i].remove();
  1667. }
  1668. }
  1669. if (this.layerDistanceList) {
  1670. for (let i = 0; i < this.layerDistanceList.length; i++) {
  1671. const layerid = this.layerDistanceList[i];
  1672. try {
  1673. if (this.map.getLayer('points' + layerid)) this.map.removeLayer('points' + layerid);
  1674. if (this.map.getLayer('line' + layerid)) this.map.removeLayer('line' + layerid);
  1675. if (this.map.getLayer('point-move' + layerid)) this.map.removeLayer('point-move' + layerid);
  1676. if (this.map.getLayer('line-move' + layerid)) this.map.removeLayer('line-move' + layerid);
  1677. } catch (error) {
  1678. console.log(error);
  1679. }
  1680. }
  1681. }
  1682. this.layerDistanceList = [];
  1683. if (this.layerAreaList) {
  1684. for (let i = 0; i < this.layerAreaList.length; i++) {
  1685. const layerid = this.layerAreaList[i];
  1686. try {
  1687. if (this.map.getLayer('points-area' + layerid)) this.map.removeLayer('points-area' + layerid);
  1688. if (this.map.getLayer('line-area' + layerid)) this.map.removeLayer('line-area' + layerid);
  1689. if (this.map.getLayer('line-area-stroke' + layerid)) this.map.removeLayer('line-area-stroke' + layerid);
  1690. if (this.map.getLayer('point-move' + layerid)) this.map.removeLayer('point-move' + layerid);
  1691. if (this.map.getLayer('line-move' + layerid)) this.map.removeLayer('line-move' + layerid);
  1692. } catch (error) {
  1693. console.log(error);
  1694. }
  1695. }
  1696. }
  1697. this.layerAreaList = [];
  1698. if (this.layerPointList) {
  1699. for (let i = 0; i < this.layerPointList.length; i++) {
  1700. const layerid = this.layerPointList[i];
  1701. try {
  1702. if (this.map.getLayer('points' + layerid)) this.map.removeLayer('points' + layerid);
  1703. if (this.map.getLayer('line-area' + layerid)) this.map.removeLayer('line-area' + layerid);
  1704. } catch (error) {
  1705. console.log(error);
  1706. }
  1707. }
  1708. }
  1709. this.layerPointList = [];
  1710. this.map.doubleClickZoom.enable();
  1711. }
  1712. };
  1713. });