| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import * as turf from '@turf/turf' |
| | | import addressLocationIcon from '@/appDataSource/leafletMapIcon/address-location.svg' |
| | | import droneIcon from '@/appDataSource/leafletMapIcon/drone-icon.svg' |
| | | import userLocationIcon from '@/appDataSource/leafletMapIcon/user-location.svg' |
| | | import layerIcon from '@/appDataSource/leafletMapIcon/layer-icon.svg' |
| | | import locationIcon from '@/appDataSource/leafletMapIcon/location-icon.svg' |
| | | import { useStore } from 'vuex' |
| | | import { showToast } from 'vant' |
| | | import L from 'leaflet' |
| | | import 'leaflet/dist/leaflet.css' |
| | | import sl from '@/appDataSource/leafletMapIcon/sl.svg' |
| | |
| | | let droneMarkersLayer = null |
| | | let eventMarkersLayer = null |
| | | let addressMarkersLayer = null |
| | | let mapLayerSourceLayer = null // 空域信息 |
| | | |
| | | const initMap = () => { |
| | | if (map) return |
| | |
| | | attributionControl: false, |
| | | doubleClickZoom: false, |
| | | editable: true, //绘制控件 |
| | | }).setView([25.992338, 114.823254], 3) |
| | | }).setView([27.117445, 114.984917], 10) |
| | | |
| | | map.whenReady(() => { |
| | | isMapReady.value = true |
| | |
| | | droneMarkersLayer = L.layerGroup([], { pane: 'drones' }).addTo(map) // 创建一个标注层,便于管理和移除 |
| | | eventMarkersLayer = L.layerGroup([], { pane: 'events' }).addTo(map) // 创建一个标注层,便于管理和移除 |
| | | addressMarkersLayer = L.layerGroup([], { pane: 'addresses' }).addTo(map) // 创建一个标注层,便于管理和移除 |
| | | // 空域信息 |
| | | mapLayerSourceLayer = L.layerGroup([], { pane: 'mapLayerSource' }).addTo(map) |
| | | |
| | | map.on('zoomend', function () { |
| | | var currentZoom = map.getZoom() |
| | |
| | | L.marker([lat, lng], { icon: droneHtmlIcon }).addTo(addressMarkersLayer) |
| | | } |
| | | |
| | | // 吉安项目-空域信息 |
| | | // 统一的飞行函数 |
| | | function flyVisual(coordinates) { |
| | | console.log(coordinates, '0000') |
| | | // 确保map对象存在且坐标有效 |
| | | if (!map || !coordinates || !coordinates.length) return; |
| | | |
| | | try { |
| | | // 改进坐标格式判断 |
| | | let isSinglePoint = false; |
| | | let latLng; |
| | | let bounds; |
| | | |
| | | // 处理不同格式的坐标输入 |
| | | if (Array.isArray(coordinates[0])) { |
| | | if (coordinates[0].length === 2) { |
| | | // 格式: [[lng, lat]] - 单个点 |
| | | isSinglePoint = true; |
| | | latLng = [coordinates[0][1], coordinates[0][0]]; // 转换为 [lat, lng] |
| | | } else if (coordinates[0].length > 2 && Array.isArray(coordinates[0][0])) { |
| | | // 格式: [[[lng, lat], [lng, lat], ...]] - 多边形 |
| | | const polygonCoords = coordinates[0].map(coord => [coord[1], coord[0]]); |
| | | bounds = L.latLngBounds(polygonCoords); |
| | | } else { |
| | | // 格式: [[lng, lat], [lng, lat], ...] - 多个点或线 |
| | | if (coordinates.length === 1) { |
| | | // 单个点 |
| | | isSinglePoint = true; |
| | | latLng = [coordinates[0][1], coordinates[0][0]]; |
| | | } else { |
| | | // 多个点 |
| | | const leafletCoords = coordinates.map(coord => [coord[1], coord[0]]); |
| | | bounds = L.latLngBounds(leafletCoords); |
| | | } |
| | | } |
| | | } else if (coordinates.length === 2) { |
| | | // 格式: [lng, lat] - 单个点 |
| | | isSinglePoint = true; |
| | | latLng = [coordinates[1], coordinates[0]]; |
| | | } |
| | | |
| | | if (isSinglePoint && latLng) { |
| | | // 单个坐标点 |
| | | map.flyTo(latLng, 10, { |
| | | duration: 1, |
| | | easeLinearity: 0.2 |
| | | }); |
| | | } else if (bounds) { |
| | | // 多边形或多个点 |
| | | map.flyToBounds(bounds, { |
| | | padding: [30, 30], |
| | | duration: 2, |
| | | maxZoom: 13 |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error('flyVisual 函数执行错误:', error); |
| | | } |
| | | } |
| | | // 点击单个飞行 |
| | | function signFlyToMarker(item) { |
| | | const positionsData = JSON.parse(item.content); |
| | | const coordinates = positionsData.coordinates; |
| | | console.log('coordinates数据:', coordinates, '类型:', typeof coordinates, '长度:', coordinates?.length); |
| | | |
| | | // 更全面的空值检查 |
| | | let hasValidCoordinates = false; |
| | | |
| | | // 检查不同格式的坐标数据 |
| | | if (Array.isArray(coordinates)) { |
| | | if (coordinates.length > 0) { |
| | | // 检查是否有有效的坐标点 |
| | | if (Array.isArray(coordinates[0])) { |
| | | if (coordinates[0].length === 2 || coordinates[0].length === 3) { |
| | | // 格式: [lng, lat] 或 [lng, lat, alt] |
| | | hasValidCoordinates = true; |
| | | } else if (Array.isArray(coordinates[0][0])) { |
| | | // 格式: [[lng, lat], [lng, lat], ...] |
| | | hasValidCoordinates = coordinates[0].length > 0; |
| | | } else if (Array.isArray(coordinates[0][0][0])) { |
| | | // 格式: [[[lng, lat], [lng, lat], ...]] |
| | | hasValidCoordinates = coordinates[0][0].length > 0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!hasValidCoordinates) { |
| | | console.log('没有有效的坐标数据,显示提示'); |
| | | return showToast('暂无区域数据'); |
| | | } |
| | | |
| | | // 单个位置飞行 |
| | | flyVisual(coordinates); |
| | | } |
| | | // 清空所有空域信息 |
| | | function mapClearAirMarker() { |
| | | if (!map) return |
| | | // 清空所有图层 |
| | | if (mapLayerSourceLayer) { |
| | | mapLayerSourceLayer.clearLayers(); |
| | | } |
| | | } |
| | | function mapAddAirMarker(data) { |
| | | // 清空所有图层 |
| | | mapClearAirMarker() |
| | | |
| | | data |
| | | .filter(item => item.content) |
| | | .forEach(item => { |
| | | const parseParsed = JSON.parse(item.content); // 转为数组 |
| | | if (parseParsed.type !== 'Polygon') return |
| | | const parsed = parseParsed.coordinates[0] |
| | | |
| | | const grouped = parsed.flat(); // 再展开 |
| | | |
| | | // 3. 计算多边形质心 |
| | | const turfPolygon = turf.polygon([parsed]); |
| | | const center = turf.centerOfMass(turfPolygon); |
| | | const centerLatLng = [center.geometry.coordinates[1], center.geometry.coordinates[0]]; |
| | | |
| | | const labelMarker = L.marker(centerLatLng, { |
| | | icon: L.divIcon({ |
| | | className: 'polygon-label', |
| | | html: ` |
| | | <div style=" |
| | | color: ${item.airspaceType === '0' ? '#008013' : '#ff0000'}; |
| | | font-weight: bold; |
| | | font-size: 12px; |
| | | font-family: 'Source Han Sans CN', 'Source Han Sans CN'; |
| | | white-space: nowrap; |
| | | "> |
| | | ${item.airspaceType === '1' ? '禁飞区' : item.airspaceType === '2' ? '危险区' : item.airspaceType === '3' ? '限制区' : item.airspaceType === '0' ? '适飞区' : '未知'} |
| | | </div> |
| | | `, |
| | | iconSize: null, // 让div决定大小 |
| | | iconAnchor: null // 不需要锚点,用CSS transform居中 |
| | | }), |
| | | interactive: false, // 标签不参与交互 |
| | | // zIndexOffset: 1000 // 确保标签在最上层 |
| | | }); |
| | | |
| | | // 将坐标数组转换为 Leaflet 格式 [lat, lng, lat, lng...] -> [[lat, lng], [lat, lng]...] |
| | | const latLngs = []; |
| | | for (let i = 0; i < grouped.length; i += 2) { |
| | | latLngs.push([grouped[i + 1], grouped[i]]); // 注意:Leaflet 是 [lat, lng] 顺序 |
| | | } |
| | | |
| | | // 创建多边形(填充区域) |
| | | const polygon = L.polygon(latLngs, { |
| | | color: item.airspaceType === '0' ? '#00ff06' : '#ff0000', |
| | | fillColor: item.airspaceType === '0' ? '#00ff06' : '#ff0000', |
| | | fillOpacity: 0.5, |
| | | weight: 0, // 隐藏多边形边框,使用折线作为边框 |
| | | }); |
| | | |
| | | // 创建折线(边框) |
| | | const polyline = L.polyline([...latLngs, latLngs[0]], { // 闭合多边形 |
| | | color: item.airspaceType === '0' ? '#00ff06' : '#ff0000', |
| | | weight: 1.5, |
| | | opacity: 1, |
| | | fill: false |
| | | }); |
| | | |
| | | // 将两个图层组合在一起 |
| | | const group = L.featureGroup([polygon, polyline, labelMarker]); |
| | | // 添加到图层组 |
| | | mapLayerSourceLayer.addLayer(group); |
| | | }); |
| | | } |
| | | |
| | | const mapClearMarker = () => { |
| | | addressMarkersLayer.clearLayers() |
| | | } |
| | |
| | | |
| | | EventBus.on('mapSetView', mapSetView) |
| | | EventBus.on('mapAddMarker', mapAddMarker) |
| | | EventBus.on('mapAddAirMarker', mapAddAirMarker) |
| | | EventBus.on('signFlyToMarker', signFlyToMarker) |
| | | EventBus.on('mapClearAirMarker', mapClearAirMarker) |
| | | EventBus.on('mapClearMarker', mapClearMarker) |
| | | |
| | | // L.circle([24, 112], { |
| | |
| | | onUnmounted(() => { |
| | | EventBus.off('mapSetView', mapSetView) |
| | | EventBus.off('mapAddMarker', mapAddMarker) |
| | | EventBus.off('mapAddAirMarker', mapAddAirMarker) |
| | | EventBus.off('signFlyToMarker', signFlyToMarker) |
| | | EventBus.off('mapClearAirMarker', mapClearAirMarker) |
| | | EventBus.off('mapClearMarker', mapClearMarker) |
| | | }) |
| | | |