homeMap.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. <template>
  2. <div id="map-container"></div>
  3. </template>
  4. <script setup>
  5. import { ref, onMounted, onUnmounted } from "vue";
  6. import AMapLoader from "@amap/amap-jsapi-loader";
  7. import eventBus from "@/api/eventBus";
  8. import { convertPointToArray } from "@/utils/index";
  9. import { deepClone } from "@/common/commonFun";
  10. import wellknown from 'wellknown';
  11. import {base_img_url,base_img_url3} from "@/api/config";
  12. const map = ref(null);
  13. const mouseTool = ref(null);
  14. const selectedArea = ref(null);
  15. const selectedPoints = ref([]);
  16. const allPoints = ref([]);
  17. const defalutAllPoints = ref([])
  18. const pointList = ref([])
  19. const mapEventType = ref('rectangle')
  20. // 自定义圆点样式
  21. const createMarkerContent = (color = 'rgba(0, 0, 0, 0.4)') => {
  22. return `
  23. <div style="
  24. width: 15px;
  25. height: 15px;
  26. background-color: ${color};
  27. border: 1px solid #fff;
  28. border-radius: 50%;
  29. ">
  30. </div>
  31. `;
  32. }
  33. //高亮样式
  34. const createMarkerImg = () => {
  35. return `
  36. <img style="width: 38px;height: 38px;" src="${require('@/assets/images/map/status/active-icon.png')}">
  37. `;
  38. }
  39. // 初始化地图
  40. const initMap = async () => {
  41. try {
  42. const AMap = await AMapLoader.load({
  43. key: "41769169f0f157e13a197e7eb0dd7b5b", // 替换为你的高德地图 Key
  44. version: "2.0", // SDK 版本
  45. plugins: ["AMap.MouseTool","AMap.MarkerCluster"], // 加载 MouseTool 插件
  46. }).then(async (AMap) => {
  47. // 创建地图实例
  48. map.value = new AMap.Map("map-container", {
  49. zoom: 18, // 初始缩放级别
  50. center: [111.01055729572, 21.7743543],
  51. layers: [
  52. // 天地图卫星
  53. new AMap.TileLayer({
  54. tileSize: 256, // 瓦片大小
  55. getTileUrl: function(x, y, z) {
  56. // 天地图影像 WMTS 地址
  57. const s = Math.floor(Math.random() * 8); // 随机选择服务器节点
  58. // return `https://t{0-7}.tianditu.gov.cn/img_c/w mts?tk=e95115c454a663cd052d96019fd83840`
  59. return `https://t${s}.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX=${z}&TILEROW=${y}&TILECOL=${x}&tk=e95115c454a663cd052d96019fd83840`;
  60. },
  61. }),
  62. //天地图路网
  63. new AMap.TileLayer({
  64. tileSize: 256, // 瓦片大小
  65. getTileUrl: function(x, y, z) {
  66. // 天地图影像 WMTS 地址
  67. const s = Math.floor(Math.random() * 8); // 随机选择服务器节点
  68. return `https://t${s}.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX=${z}&TILEROW=${y}&TILECOL=${x}&tk=e95115c454a663cd052d96019fd83840`;
  69. },
  70. }),
  71. //正射影像
  72. new AMap.TileLayer({
  73. tileUrl: base_img_url + 'map/lby/[z]/[x]/[y].png',
  74. zIndex:2
  75. }),
  76. new AMap.TileLayer({
  77. tileUrl: base_img_url3 + 'map/lby/[z]/[x]/[y].png',
  78. zIndex:2
  79. })
  80. ],
  81. });
  82. // // 初始化点聚合
  83. // const cluster = new AMap.MarkerCluster(map, markers, {
  84. // gridSize: 60, // 聚合网格像素大小
  85. // maxZoom: 15, // 最大聚合级别
  86. // renderClusterMarker: customizeCluster // 自定义聚合样式
  87. // });
  88. // 初始化 MouseTool
  89. mouseTool.value = new AMap.MouseTool(map.value);
  90. // startRectangleSelection()
  91. // 监听框选完成事件
  92. mouseTool.value.on("draw", (event) => {
  93. if(mapEventType.value==='click') return
  94. const { obj} = event;
  95. if (obj instanceof AMap.Rectangle) {
  96. selectedArea.value = obj;
  97. map.value.remove(selectedArea.value);
  98. checkPointsInArea(); // 检查框选区域内的点位
  99. }
  100. });
  101. });
  102. } catch (error) {
  103. console.error("地图加载失败:", error);
  104. }
  105. };
  106. //重置数据
  107. const resetData = () =>{
  108. allPoints.value = defalutAllPoints.value
  109. allPoints.value.forEach(item =>{
  110. item.icon.setContent(createMarkerContent(item.color))
  111. item.icon.setOffset(new window.AMap.Pixel(-7, -7))
  112. item.type = 'defalutIcon'
  113. })
  114. }
  115. //初始化数据
  116. const initData = (data) =>{
  117. allPoints.value = data
  118. defalutAllPoints.value = []
  119. totalList.value.forEach(item => map.value.remove(item.icon))
  120. totalList.value = []
  121. allPoints.value.forEach(item =>{
  122. item.icon = new window.AMap.Marker({
  123. position: item.lnglat,
  124. map: map.value,
  125. content: createMarkerContent(item.color),
  126. offset: new window.AMap.Pixel(-7, -7),
  127. extData: { id: item.id, priority: item.status > item.noImg > item.wys } // 标记优先级
  128. });
  129. item.type = 'defalutIcon'
  130. // 绑定点击事件
  131. item.icon.on('click', () => {
  132. mapEventType.value = 'click'
  133. if(item.type === 'defalutIcon'){
  134. if (startDraw.value) {
  135. item.icon.setContent(createMarkerImg());
  136. item.icon.setOffset(new window.AMap.Pixel(-18, -18))
  137. item.type = 'activeIcon'
  138. const arr = pointList.value.filter(ele => ele.id === item.id)
  139. if(arr.length===0){
  140. pointList.value.push(item)
  141. }
  142. }
  143. }else{
  144. item.icon.setContent(createMarkerContent(item.color));
  145. item.icon.setOffset(new window.AMap.Pixel(-7, -7))
  146. item.type = 'defalutIcon'
  147. }
  148. console.log('item', item);
  149. eventBus.emit("clickMapPoint", item)
  150. setTimeout(()=>{
  151. mapEventType.value = 'draw'
  152. },100)
  153. });
  154. defalutAllPoints.value.push(item)
  155. })
  156. // 初始化点聚合
  157. // console.log('data',data);
  158. // const cluster = new window.AMap.MarkerCluster(map.value, data, {
  159. // gridSize: 30, // 聚合网格像素大小
  160. // // maxZoom: 15, // 最大聚合级别
  161. // // renderClusterMarker: customizeCluster // 自定义聚合样式
  162. // });
  163. setTimeout(()=>{
  164. map.value.setFitView(null, false, [0, 0, 0, 0]);
  165. },100)
  166. // 范围
  167. // const p1 = [
  168. // [111.00745562137854, 21.77564355219471],
  169. // [111.00982318507914, 21.77557930674749],
  170. // [111.01133652227907, 21.776531091149934],
  171. // [111.0125762214629, 21.775945743742568],
  172. // [111.01290458708189, 21.775241423284797],
  173. // [111.01341617119812, 21.773759019078113],
  174. // [111.01320915809049, 21.77329502418189],
  175. // [111.01209794980082, 21.772538355582128],
  176. // [111.01117233946951, 21.772414623609905],
  177. // [111.00909744947245, 21.772878618505956],
  178. // [111.00744610353439, 21.774094523079953],
  179. // [111.00745562137854, 21.77564355219471]
  180. // ]
  181. // addPolygon(p1)
  182. }
  183. const startDraw = ref(false)
  184. // 开始框选
  185. const startRectangleSelection = () => {
  186. if (mouseTool.value) {
  187. startDraw.value = true
  188. mouseTool.value.rectangle({
  189. strokeColor: "#ffffff", // 框选区域边框颜色
  190. strokeOpacity: 1, // 边框透明度
  191. strokeWeight: 2, // 边框宽度
  192. fillColor: "#000000", // 填充颜色
  193. fillOpacity: 0.5, // 填充透明度
  194. }); // 启用矩形框选工具
  195. map.value.setDefaultCursor("crosshair");
  196. }
  197. };
  198. // 停止绘制矩形
  199. const stopDrawRectangle = () => {
  200. mouseTool.value.close(); // 关闭 mouseTool
  201. map.value.setDefaultCursor("default");
  202. };
  203. // 清除框选
  204. const clearSelection = () => {
  205. if (selectedArea.value) {
  206. map.value.remove(selectedArea.value); // 移除框选区域
  207. selectedArea.value = null;
  208. }
  209. resetData()
  210. selectedPoints.value = []; // 清空选中的点位
  211. };
  212. // 检查框选区域内的点位
  213. const checkPointsInArea = () => {
  214. if (!selectedArea.value) return;
  215. const bounds = selectedArea.value.getBounds(); // 获取框选区域的边界
  216. selectedPoints.value = allPoints.value.filter((point) => {
  217. return bounds.contains(point.lnglat); // 判断点位是否在框选区域内
  218. });
  219. selectedPoints.value.forEach(item =>{
  220. if(item.type==='defalutIcon'){
  221. item.icon.setContent(createMarkerImg())
  222. item.icon.setOffset(new window.AMap.Pixel(-18, -18))
  223. item.type = 'activeIcon'
  224. }else{
  225. item.icon.setContent(createMarkerContent(item.color))
  226. item.icon.setOffset(new window.AMap.Pixel(-7, -7))
  227. item.type = 'defalutIcon'
  228. }
  229. })
  230. };
  231. const totalList = ref([])
  232. function getSelectPoint(){
  233. totalList.value = selectedPoints.value.concat(pointList.value)
  234. const arr = totalList.value.filter(item =>item.type==='activeIcon')
  235. return arr
  236. }
  237. function getRegionList(farmId) {
  238. VE_API.region.list({farmId}).then(({data}) =>{
  239. data.map(item => {
  240. addPolygon(wktToAmapPolygon(item.wkt))
  241. })
  242. })
  243. }
  244. function addPolygon(data) {
  245. console.log('dddddd', data);
  246. let polygon = new window.AMap.Polygon({
  247. path: data,
  248. fillColor: '#FFE17E',
  249. strokeOpacity: 1,
  250. fillOpacity: 0.1,
  251. strokeColor: 'rgba(255, 255, 255, 0.3)',
  252. strokeWeight: 1,
  253. strokeStyle: 'solid',
  254. // strokeDasharray: [5, 5],
  255. });
  256. map.value.add(polygon)
  257. }
  258. defineExpose({getSelectPoint,initData,startRectangleSelection,stopDrawRectangle,clearSelection, getRegionList})
  259. // 组件挂载时初始化地图
  260. onMounted(() => {
  261. initMap()
  262. });
  263. onUnmounted(()=>{
  264. map.value.destroy()// 销毁地图实例以释放资源
  265. })
  266. function wktToAmapPolygon(wkt) {
  267. const geoJSON = wellknown.parse(wkt);
  268. if (!geoJSON || geoJSON.type !== 'MultiPolygon') {
  269. console.error('Invalid WKT or not a MULTIPOLYGON');
  270. return [];
  271. }
  272. // MULTIPOLYGON 的坐标结构:[[[ [环1], [环2], ... ]]],取第一个环
  273. return geoJSON.coordinates[0][0].map(coord => [coord[0], coord[1]]);
  274. }
  275. </script>
  276. <style scoped>
  277. #map-container {
  278. width: 100%;
  279. height: 100%;
  280. }
  281. </style>