authenticMap.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. import config from "@/api/config.js";
  2. import * as KMap from "@/utils/ol-map/KMap";
  3. import * as util from "@/common/ol_common.js";
  4. import Style from "ol/style/Style";
  5. import Icon from "ol/style/Icon";
  6. import VectorLayer from "ol/layer/Vector.js";
  7. import WKT from "ol/format/WKT.js";
  8. import { reactive } from "vue";
  9. import Point from "ol/geom/Point.js";
  10. import Feature from "ol/Feature";
  11. import { newPoint } from "@/utils/map.js";
  12. import { Fill, Text,Circle,Stroke } from "ol/style";
  13. import { getArea } from "ol/sphere.js";
  14. import * as proj from "ol/proj";
  15. import proj4 from "proj4";
  16. import { register } from "ol/proj/proj4";
  17. import GeometryCollection from 'ol/geom/GeometryCollection.js';
  18. import { ElMessage } from "element-plus";
  19. import { useStore } from "vuex";
  20. import {bboxToFeature} from "../../utils/map";
  21. import TileLayer from 'ol/layer/Tile';
  22. import XYZ from 'ol/source/XYZ.js';
  23. import * as turf from "@turf/turf";
  24. proj4.defs(
  25. "EPSG:38572",
  26. "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs"
  27. );
  28. register(proj4);
  29. export let mapData = reactive({
  30. isEdit: false,
  31. isEditArea: false,
  32. curPointData: {},
  33. point: null,
  34. selectPointArr: [],
  35. isPointHide: null,
  36. disabledForm : false,
  37. selectPoint:'',
  38. pointIndex:-1
  39. });
  40. function resetMapData(){
  41. mapData.isEdit= false
  42. mapData.isEditArea= false
  43. mapData.curPointData= {}
  44. mapData.point= null
  45. mapData.selectPointArr= []
  46. mapData.isPointHide= null
  47. mapData.disabledForm= false
  48. mapData.selectPoint= ''
  49. mapData.pointIndex=-1
  50. }
  51. /**
  52. * @description 地图层对象
  53. */
  54. class AuthenticMap {
  55. constructor() {
  56. let that = this;
  57. that.store = useStore();
  58. let vectorStyle = new KMap.VectorStyle();
  59. this.vectorStyle = vectorStyle;
  60. // 位置图标
  61. this.clickPointLayer = new KMap.VectorLayer("clickPointLayer", 9999, {
  62. style: (f) => {
  63. const style1 = new Style({
  64. image: new Icon({
  65. src: require(`@/assets/images/map/${f.get("icon")}-icon.png`),
  66. scale: 0.45,
  67. }),
  68. });
  69. const style2 = new Style({
  70. text: new Text({
  71. font: "16px sans-serif",
  72. text: f.get("masterName"),
  73. offsetY: -40,
  74. padding: [4, 3, 2, 106],
  75. fill: new Fill({ color: "#fff" }), // 字体颜色
  76. }),
  77. });
  78. const style3 = new Style({
  79. image: new Icon({
  80. src: require(`@/assets/images/map/${f.get("iconBg")}.png`),
  81. scale: 0.45,
  82. displacement: [0, 90],
  83. }),
  84. });
  85. return [style1, style2, style3];
  86. },
  87. });
  88. this.locationLayer = new KMap.VectorLayer("locationLayer", 9999, {
  89. style: () => {
  90. return new Style({
  91. image: new Icon({
  92. src: require("@/assets/images/map/location.png"),
  93. scale: 0.45,
  94. }),
  95. });
  96. },
  97. });
  98. this.selectPointLayer = new KMap.VectorLayer("selectPointLayer", 10000, {
  99. style: () => {
  100. return new Style({
  101. // geometry: new Point(coord[0][i]),
  102. image: new Circle({
  103. radius: 6,
  104. fill: new Fill({
  105. color: 'red'
  106. }),
  107. stroke: new Stroke({
  108. color: '#fff',
  109. width: 1
  110. }),
  111. }),
  112. });
  113. },
  114. });
  115. // 存储绘制的地块特征
  116. // this.drawnFeatures = [];
  117. }
  118. initMap(location, target) {
  119. let that = this
  120. let level = 16;
  121. let coordinate = util.wktCastGeom(location).getFirstCoordinate();
  122. this.kmap = new KMap.Map(
  123. target,
  124. level,
  125. coordinate[0],
  126. coordinate[1],
  127. null,
  128. 6,
  129. 22
  130. );
  131. this.kmap.initDraw(async (e) => {
  132. if (e.type === "drawend") {
  133. mapData.isEdit = true;
  134. mapData.point = e.feature;
  135. // 计算平面面积
  136. const polygon = e.feature.getGeometry();
  137. const coordinates = polygon.getCoordinates()[0];
  138. // 获取真实高程数据
  139. const planeArea = getArea(polygon);
  140. const elevationData = await this.fetchElevationData(coordinates);
  141. // 计算斜面面积
  142. const slopeArea = this.calculateSlopeArea(coordinates, elevationData);
  143. console.log('斜面面积: ${slopeArea.toFixed(2)} 平方米',planeArea, slopeArea)
  144. }
  145. });
  146. this.kmap.modifyDraw((e) => {
  147. if(e.type === "modifyend"){
  148. mapData.isEditArea = false;
  149. mapData.isEditArea = true;
  150. }
  151. }, function(e){
  152. let f = null
  153. that.kmap.map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
  154. f= feature
  155. },{hitTolerance:10});
  156. if(!f){
  157. return false
  158. }
  159. let res = f.get("id") === mapData.curPointData.id || !f.get("id")
  160. if(!res){
  161. ElMessage.warning("编辑中")
  162. }
  163. console.log('f.get("height")',f);
  164. res = f.get("height").length>0?false:res
  165. return res
  166. });
  167. this.kmap.addLayer(this.clickPointLayer.layer);
  168. this.kmap.addLayer(this.locationLayer.layer);
  169. this.kmap.addLayer(this.selectPointLayer.layer);
  170. this.addMapSingerClick();
  171. const demLayer = new TileLayer({
  172. source: new XYZ({
  173. url: 'https://api.maptiler.com/tiles/terrain-rgb/{z}/{x}/{y}.png?key=pk.eyJ1IjoiZ3VvemhlbnNodSIsImEiOiJjbTdyYWVmcmYxNGQ3MmpvaGJyejJiMDZpIn0.NQ-ey98NqQH1zmpe9ky9XQ',
  174. crossOrigin: 'anonymous'
  175. })
  176. });
  177. this.kmap.addLayer(demLayer);
  178. }
  179. // **获取高程数据**
  180. async fetchElevationData(coordinates) {
  181. const MAPBOX_TOKEN = 'pk.eyJ1IjoiZ3VvemhlbnNodSIsImEiOiJjbTdyYWVmcmYxNGQ3MmpvaGJyejJiMDZpIn0.NQ-ey98NqQH1zmpe9ky9XQ';
  182. const elevationData = [];
  183. for (let i = 0; i < coordinates.length; i++) {
  184. const [lon, lat] = coordinates[i];
  185. // const url = `https://api.mapbox.com/v4/mapbox.terrain-rgb/${lon},${lat}.json?access_token=${MAPBOX_TOKEN}`;
  186. const url = `https://api.mapbox.com/v4/mapbox.terrain-rgb/tilequery/${-74.5},${40}.json?access_token=${
  187. MAPBOX_TOKEN
  188. }`
  189. try {
  190. const response = await fetch(url);
  191. const data = await response.json();
  192. if (data.elevation) {
  193. elevationData.push(data.elevation);
  194. } else {
  195. elevationData.push(0); // 失败时默认高程 0
  196. }
  197. } catch (error) {
  198. console.error("获取高程数据失败:", error);
  199. elevationData.push(0);
  200. }
  201. }
  202. return elevationData;
  203. }
  204. // **计算斜面面积**
  205. calculateSlopeArea(coordinates, elevationData) {
  206. let totalArea = 0;
  207. for (let i = 0; i < coordinates.length - 1; i++) {
  208. const p1 = coordinates[i];
  209. const p2 = coordinates[i + 1];
  210. const dx = p2[0] - p1[0];
  211. const dy = p2[1] - p1[1];
  212. const dz = elevationData[i + 1] - elevationData[i];
  213. const baseLength = Math.sqrt(dx * dx + dy * dy);
  214. const slopedLength = Math.sqrt(baseLength * baseLength + dz * dz);
  215. totalArea += slopedLength * baseLength * 0.5; // 三角形面积
  216. }
  217. return totalArea;
  218. }
  219. fit(geometriesWkt){
  220. let geometries = []
  221. let f = new WKT();
  222. for(let wkt of geometriesWkt){
  223. geometries.push(f.readGeometry(wkt))
  224. }
  225. let extent = new GeometryCollection(geometries).getExtent()
  226. this.kmap.fit(extent)
  227. }
  228. //撤销上一个点
  229. undoLastDraw() {
  230. // const coordinates = this.kmap.getLayerFeatures()[0].getGeometry().getCoordinates()
  231. // coordinates[0][0].pop()
  232. // this.kmap.getLayerFeatures()[0].getGeometry().getCoordinates(coordinates)
  233. }
  234. // 取消地块
  235. cancelDraw() {
  236. this.kmap.polygonLayer.source.removeFeature(mapData.point);
  237. }
  238. // 添加点位
  239. addPoint(points) {
  240. const arrPoints = [];
  241. if (points && points.length > 0) {
  242. points.forEach((item) => {
  243. let f = newPoint({ ...item, icon: "point", iconBg: "name-bg" }, "point");
  244. arrPoints.push(f);
  245. });
  246. this.clickPointLayer.source.addFeatures(arrPoints);
  247. }
  248. }
  249. // 设置地图中心点位
  250. setMapCenter(v,isPoint = true) {
  251. let arrayOfNumbers = [];
  252. const arrayOfStrings = v.split(",");
  253. arrayOfNumbers = [arrayOfStrings[1], arrayOfStrings[0]];
  254. this.kmap.map.getView().setCenter(arrayOfNumbers);
  255. if(!isPoint) return
  256. this.locationLayer.source.clear();
  257. let point = new Feature(new Point(arrayOfNumbers));
  258. this.locationLayer.addFeature(point);
  259. }
  260. // 开始勾画
  261. startDraw() {
  262. this.kmap.setDefaultCursor("crosshair");
  263. this.kmap.startDraw();
  264. this.kmap.endModify();
  265. }
  266. //结束勾画
  267. endDraw() {
  268. this.kmap.endDraw();
  269. this.kmap.endModify();
  270. }
  271. // 开始编辑
  272. startModify(type, feature) {
  273. this.kmap.startModify();
  274. this.kmap.endDraw();
  275. if(type==='upload'){
  276. mapData.point = feature
  277. mapData.point.set("icon", "point-act");
  278. this.kmap.polygonStyle(mapData.point);
  279. mapData.isPointHide = mapData.point;
  280. }else{
  281. mapData.point.set("icon", "point-act");
  282. }
  283. }
  284. //结束编辑
  285. endModify() {
  286. this.kmap.endModify();
  287. }
  288. // 清空单个数据
  289. clearMapData(name, val, id) {
  290. name && (mapData[name] = val);
  291. this.clickPointLayer.source.forEachFeature((feature) => {
  292. feature.set("icon", "point");
  293. feature.set("iconBg", "name-bg");
  294. });
  295. const points = this.kmap.getLayerFeatures();
  296. points.forEach((feature) => {
  297. feature.set("icon", "point");
  298. });
  299. resetMapData()
  300. }
  301. //全选
  302. allSelect(ids){
  303. let arr = []
  304. this.clickPointLayer.source.forEachFeature((feature) => {
  305. if(!ids || ids.findIndex((id)=> id == feature.get('id')) > -1){
  306. feature.set("icon", "point-act");
  307. feature.set("iconBg", "name-act-bg");
  308. }
  309. });
  310. const points = this.kmap.getLayerFeatures();
  311. points.forEach((feature) => {
  312. if(!ids || ids.findIndex((id)=> id == feature.get('id')) > -1) {
  313. feature.set("icon", "point-act");
  314. this.kmap.polygonStyle(feature);
  315. mapData.isPointHide = feature;
  316. arr.push(feature)
  317. }
  318. });
  319. mapData.selectPointArr = arr;
  320. }
  321. //no全选
  322. allUnSelect(){
  323. this.clickPointLayer.source.forEachFeature((feature) => {
  324. feature.set("icon", "point");
  325. feature.set("iconBg", "name-bg");
  326. });
  327. const points = this.kmap.getLayerFeatures();
  328. points.forEach((feature) => {
  329. feature.set("icon", "point");
  330. this.kmap.polygonStyle(feature);
  331. mapData.isPointHide = feature;
  332. });
  333. mapData.selectPointArr = [];
  334. }
  335. //选中高亮样式
  336. //选中点位方法
  337. selectPonitFun(map,evt){
  338. let that = this
  339. map.forEachFeatureAtPixel(evt.pixel, function (f, layer) {
  340. let d = 0.0002
  341. let buff = bboxToFeature(evt.pixel[0] - d,evt.pixel[1] - d, evt.pixel[0]+ d, evt.pixel[1] + d)
  342. if (
  343. layer instanceof VectorLayer &&
  344. (
  345. layer.get("name") === "defaultPolygonLayer")
  346. ) {
  347. const fs = map.getFeaturesAtPixel(evt.pixel);
  348. let lonlat = map.getCoordinateFromPixel(evt.pixel);
  349. if (fs.length > 0) {
  350. const feature = fs[0];
  351. const geometry = feature.getGeometry();
  352. let coordinates = geometry.getCoordinates()[0]; // 获取多边形的顶点坐标
  353. ElMessage.success("已选择该点位")
  354. let list
  355. if(mapData.selectPointArr.length>0){
  356. list = f.getGeometry().getCoordinates();
  357. }else{
  358. list = mapData.point.getGeometry().getCoordinates();
  359. }
  360. coordinates = list[0][0]
  361. let arr = []
  362. let i = 0;let curDistance = 0
  363. for(let subArray of coordinates){
  364. let from = turf.point(subArray);
  365. let to = turf.point(lonlat);
  366. let options = { units: "miles" };
  367. let distance = turf.distance(from, to, options);
  368. if(distance < 0.1){
  369. if(curDistance == 0){
  370. arr = subArray
  371. curDistance = distance
  372. mapData.pointIndex = i
  373. }else if(curDistance > distance){
  374. arr = subArray
  375. curDistance = distance
  376. mapData.pointIndex = i
  377. }
  378. }
  379. i++
  380. }
  381. that.selectPointLayer.refresh();
  382. let point = new Feature(new Point(arr));
  383. that.selectPointLayer.addFeature(point);
  384. mapData.selectPoint = coordinates
  385. }
  386. }
  387. });
  388. }
  389. // 地图点击事件
  390. addMapSingerClick() {
  391. let that = this;
  392. that.kmap.on("singleclick", (evt) => {
  393. if(mapData.curPointData.id && !mapData.disabledForm && mapData.selectPointArr.length===1){
  394. that.selectPonitFun(that.kmap.map,evt)
  395. return;
  396. }
  397. if(!mapData.curPointData.id && mapData.isEdit){
  398. that.selectPonitFun(that.kmap.map,evt)
  399. return;
  400. }
  401. //判断是否是导入的数据
  402. if(that.store.state.authentic.isEditStatus){
  403. that.selectPonitFun(that.kmap.map,evt)
  404. return;
  405. }
  406. let num = 0;
  407. that.kmap.map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) {
  408. // 点击的图层是否是VectorLayer
  409. if (
  410. layer instanceof VectorLayer &&
  411. (layer.get("name") === "clickPointLayer" ||
  412. layer.get("name") === "defaultPolygonLayer")
  413. ) {
  414. // 每次点击,只走一遍该方法
  415. num = num + 1;
  416. if (num === 1) {
  417. that.getSelectPointArr(feature.get("id"));
  418. that.kmap.endDraw();
  419. }
  420. }
  421. });
  422. });
  423. }
  424. clearSelectPoint(){
  425. this.selectPointLayer.source.clear();
  426. }
  427. setPoint(name) {
  428. const arr = mapData.selectPointArr.filter(
  429. (item) => item.values_.icon === "point-act"
  430. );
  431. if (arr.length > 0) {
  432. mapData.point = arr[0];
  433. mapData.point.set("icon", name);
  434. mapData.isPointHide.set("icon", name);
  435. mapData.point.set("iconBg", "name-bg");
  436. }
  437. if (arr.length === 1) {
  438. mapData.selectPointArr = [];
  439. }
  440. }
  441. //添加地块
  442. setAreaGeometry(geometryArr) {
  443. let that = this;
  444. let res = []
  445. geometryArr.map((item) => {
  446. item.icon = "point";
  447. item.iconHide = "false";
  448. res.push(that.kmap.setLayerWkt(item.featureWkt, item));
  449. });
  450. return res
  451. }
  452. deletePointFun(point,callback){
  453. const coordinates = point.getGeometry().getCoordinates();
  454. let array = coordinates[0][0]
  455. // 如果点存在,则删除它
  456. if (mapData.pointIndex > -1) {
  457. array.splice(mapData.pointIndex, 1); // 删除点
  458. // 更新多边形的坐标
  459. point.getGeometry().setCoordinates(coordinates); // 更新几何形状
  460. mapData.pointIndex = -1
  461. }
  462. this.clearSelectPoint()
  463. callback && callback(mapData.pointIndex)
  464. }
  465. // 移除点的功能
  466. removePoint(callback) {
  467. // 获取多边形的所有坐标
  468. const features = this.kmap.getLayerFeatures()
  469. if(mapData.selectPointArr.length>0){
  470. features.forEach(item =>{
  471. if(item.get("id")===mapData.selectPointArr[0].get("id")){
  472. this.deletePointFun(item,callback)
  473. }
  474. })
  475. }else{
  476. this.deletePointFun(mapData.point,callback)
  477. }
  478. }
  479. // 获取所有选中点位
  480. getSelectPointArr(id) {
  481. const arr = [];
  482. this.clickPointLayer.source.forEachFeature((feature) => {
  483. if (feature.get("id") === id) {
  484. // 修改当前点位高亮
  485. const icon = feature.get("icon") === "point" ? "point-act" : "point";
  486. const iconBg =
  487. feature.get("iconBg") === "name-bg" ? "name-act-bg" : "name-bg";
  488. feature.set("icon", icon);
  489. feature.set("iconBg", iconBg);
  490. mapData.point = feature;
  491. mapData.curPointData = feature.values_;
  492. }
  493. if (feature.get("icon") === "point-act") {
  494. arr.push(feature);
  495. }
  496. });
  497. const points = this.kmap.getLayerFeatures();
  498. points.forEach((feature) => {
  499. if (feature.get("id") === id) {
  500. const icon = feature.get("icon") === "point" ? "point-act" : "point";
  501. feature.set("icon", icon);
  502. this.kmap.polygonStyle(feature);
  503. mapData.isPointHide = feature;
  504. }
  505. });
  506. mapData.selectPointArr = arr;
  507. }
  508. hidePoint() {
  509. const feature = mapData.isPointHide;
  510. feature.set("iconHide", "true");
  511. this.kmap.polygonStyle(feature);
  512. }
  513. clearLayer() {
  514. this.clickPointLayer.source.clear();
  515. this.kmap.polygonLayer.source.clear();
  516. }
  517. addLayer() {
  518. this.kmap.addLayer(this.kmap.polygonLayer.layer);
  519. this.kmap.addLayer(this.clickPointLayer.layer);
  520. }
  521. //获取地块信息
  522. getAreaGeometry(type) {
  523. let features = null
  524. if(type==='add') {
  525. features = [mapData.point]
  526. }else if(type==='upload'){
  527. features = this.kmap.getLayerFeatures()
  528. }else{
  529. if(mapData.curPointData.id){
  530. features = [this.kmap.getFeatureById(mapData.curPointData.id)];
  531. }else{
  532. features = [mapData.point]
  533. }
  534. }
  535. let geometryArr = [];
  536. let area = 0;
  537. // 获取图层上的Polygon,转成geoJson用于回显
  538. features.forEach((item) => {
  539. geometryArr.push({ featureWkt: new WKT().writeFeature(item) });
  540. let geom = item.getGeometry().clone();
  541. geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"));
  542. let areaItem = getArea(geom);
  543. area = (areaItem + areaItem / 2) / 1000;
  544. });
  545. return { geometryArr, area: area.toFixed(2) };
  546. }
  547. /**
  548. address
  549. farmName
  550. masterName
  551. masterTel
  552. speciesTypeName
  553. ""
  554. * @param form
  555. */
  556. search(form){
  557. const points = this.kmap.getLayerFeatures();
  558. let arr = []
  559. let geomWkt = []
  560. points.forEach((feature) => {
  561. let condition = []
  562. if(form.address != ''){
  563. condition.push(feature.get("address").includes(form.address))
  564. }
  565. if(form.farmName != ''){
  566. const text = feature.get("farmName")
  567. if(text!==form.farmName) return
  568. condition.push(feature.get("farmName").includes(form.farmName))
  569. }
  570. if(form.masterName != ''){
  571. const text = feature.get("masterName")
  572. if(text!==form.masterName) return
  573. condition.push(feature.get("masterName").includes(form.masterName))
  574. }
  575. if(form.masterTel != ''){
  576. const text = feature.get("masterTel")
  577. if(text!==form.masterTel) return
  578. condition.push(feature.get("masterTel").includes(form.masterTel))
  579. }
  580. if(form.speciesTypeName != ''){
  581. const text = feature.get("speciesTypeName")
  582. if(text!==form.speciesTypeName) return
  583. condition.push(feature.get("speciesTypeName").includes(form.speciesTypeName))
  584. }
  585. let b = false
  586. for(let item of condition){
  587. if(item){
  588. b = true
  589. }else{
  590. b = false
  591. break
  592. }
  593. }
  594. if(b){
  595. arr.push(feature.get("id"))
  596. geomWkt.push(new WKT().writeGeometry(feature.getGeometry()))
  597. }
  598. });
  599. this.allUnSelect()
  600. this.allSelect(arr)
  601. if(geomWkt.length>0){
  602. this.fit(geomWkt)
  603. }
  604. }
  605. }
  606. export default AuthenticMap;