| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917 |
- <template>
- <div class="map-page">
- <div ref="mapContainer"
- class="map-container"
- ></div>
- <div v-if="error" class="error">{{ error }}</div>
- <!-- 覆盖层控制 -->
- <!-- <div class="control-panel">
- <label>
- <input type="checkbox" v-model="state.showOverlay" @change="toggleOverlay" />
- 显示土壤类型覆盖
- </label>
- </div> -->
- <div class="control-panel">
- <label>
- <input type="checkbox" v-model="state.showSoilTypes" @change="toggleSoilTypeLayer" />
- 显示韶关市评估单元
- </label>
- <label>
- <input type="checkbox" v-model="state.showSurveyData" @change="toggleSurveyDataLayer" />
- 显示韶关市调查数据
- </label>
- <!-- 截图控制 -->
- <div class="export-controls">
- <button @click="exportMapImage" :disabled="!isMapReady">
- {{ isExporting ? '生成中...' : '导出截图' }}
- </button>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
- import html2canvas from 'html2canvas'
- const isExporting = ref(false)
- const isMapReady = ref(false)
- const exportSettings = reactive({
- quality: 0.9,
- showMapControls: false,
- showWatermark: true
- })
- const mapContainer = ref(null)
- const activeMarker = ref(null)
- const error = ref(null)
- let activeTempMarker = ref(null)
- let infoWindow = ref(null)
- let map = null
- let markersLayer = null
- let overlay = null
- const state = reactive({
- showOverlay: false,
- showSoilTypes: true,
- showSurveyData: true,
- excelData: [],
- lastTapTime: 0
- })
- let soilTypeLayer = null
- let geoJSONLayer;
- let currentInfoWindow = null;
- let surveyDataLayer = ref(null);
- let multiPolygon;
- const categoryColors = { // 分类颜色配置
- '优先保护类': '#00C853', // 绿色
- '安全利用类': '#FFD600', // 黄色
- '严格管控类': '#D50000' // 红色
- };
- const tMapConfig = reactive({
- key: import.meta.env.VITE_TMAP_KEY, // 请替换为你的开发者密钥
- geocoderURL: 'https://apis.map.qq.com/ws/geocoder/v1/'
- })
- const loadSDK = () => {
- return new Promise((resolve, reject) => {
- if (window.TMap?.service?.Geocoder) {
- TMap.value = window.TMap
- return resolve(window.TMap)
- }
- const script = document.createElement('script')
- script.src = `https://map.qq.com/api/gljs?v=2.exp&libraries=basic,service,vector&key=${tMapConfig.key}&callback=initTMap`
- window.initTMap = () => {
- if (!window.TMap?.service?.Geocoder) {
- reject(new Error('地图SDK加载失败'))
- return
- }
- TMap.value = window.TMap
- resolve(window.TMap)
- }
- script.onerror = (err) => {
- reject(`地图资源加载失败: ${err.message}`)
- document.head.removeChild(script)
- }
- document.head.appendChild(script)
- })
- }
- // 初始化数据
- const initData = () => {
- state.excelData = [
- { 土壤编号: "土1", 地点: "广西武鸣", dust_emissions: 5.34, longitude: 106.476143, latitude: 23.891756 },
- { 土壤编号: "土2", 地点: "广西河池", dust_emissions: 3.96, longitude: 107.476143, latitude: 24.891756 },
- { 土壤编号: "土3", 地点: "海南澄迈老城镇罗驿村委会罗驿洋", dust_emissions: 4.56, longitude: 110.125, latitude: 19.901756 },
- { 土壤编号: "土4", 地点: "广东江门新会", dust_emissions: 4.0, longitude: 109.476143, latitude: 22.461756 },
- { 土壤编号: "土5", 地点: "广州增城Z6", dust_emissions: 4.77, longitude: 110.476143, latitude: 21.891756 },
- { 土壤编号: "土6", 地点: "广州增城Z8", dust_emissions: 4.59, longitude: 111.476143, latitude: 22.891756 },
- { 土壤编号: "土7", 地点: "湖南岳阳", dust_emissions: 5.14, longitude: 112.476143, latitude: 23.891756 },
- { 土壤编号: "土8", 地点: "广东韶关武江", dust_emissions: 5.07, longitude: 113.476143, latitude: 24.891756 },
- { 土壤编号: "土9", 地点: "海南临高头星村", dust_emissions: 4.12, longitude: 109.684993, latitude: 19.83774 },
- { 土壤编号: "土10", 地点: "海南临高周礼村", dust_emissions: 5.0, longitude: 109.710703, latitude: 19.89222 },
- { 土壤编号: "土11", 地点: "海南澄迈金江", dust_emissions: 4.6, longitude: 110.069537, latitude: 19.81189 },
- { 土壤编号: "土12", 地点: "海南临高南贤村", dust_emissions: 4.2, longitude: 109.768714, latitude: 19.874323 },
- { 土壤编号: "土13", 地点: "海南澄迈金江北让村", dust_emissions: 4.5, longitude: 110.096765, latitude: 19.814288 },
- { 土壤编号: "土14", 地点: "广西扶绥", dust_emissions: 4.71, longitude: 107.7717789, latitude: 22.5166902 },
- { 土壤编号: "土15", 地点: "广西江州", dust_emissions: 4.31, longitude: 107.56347787, latitude: 22.6022203 },
- { 土壤编号: "土16", 地点: "广西龙州", dust_emissions: 5.15, longitude: 106.7870847, latitude: 22.3496497 },
- { 土壤编号: "土17", 地点: "广西大新", dust_emissions: 4.71, longitude: 107.0230641, latitude: 22.5857946 },
- { 土壤编号: "土18", 地点: "湖南岳阳荣家湾", dust_emissions: 5.04, longitude: 113.059629, latitude: 29.267061 },
- { 土壤编号: "土19", 地点: "湖南长沙", dust_emissions: 5.08, longitude: 113.059629, latitude: 28.440613 },
- { 土壤编号: "土20", 地点: "浙江", dust_emissions: 4.8, longitude: 111.45527, latitude: 24.395235 },
- { 土壤编号: "土21", 地点: "云南陆良", dust_emissions: 4.67, longitude: 112.45527, latitude: 25.395235 },
- { 土壤编号: "土22", 地点: "南昌横龙镇南园组", dust_emissions: 4.8, longitude: 113.45527, latitude: 26.395235 },
- { 土壤编号: "土23", 地点: "南昌横龙枫塘南园", dust_emissions: 5.1, longitude: 114.45527, latitude: 27.395235 },
- { 土壤编号: "土24", 地点: "南昌横龙镇院塘村", dust_emissions: 4.27, longitude: 114.852, latitude: 27.3947 },
- { 土壤编号: "土25", 地点: "江西山庄乡秀水村黄田组", dust_emissions: 4.27, longitude: 114.852, latitude: 27.5247 },
- { 土壤编号: "土26", 地点: "贵州双星村", dust_emissions: 4.7, longitude: 106.852, latitude: 27.3147},
- { 土壤编号: "土27", 地点: "湖南永州八宝镇唐家州", dust_emissions: 4.57, longitude: 113.952, latitude: 26.08147 },
- { 土壤编号: "土28", 地点: "湖南永州金洞", dust_emissions: 5.3, longitude: 112.1564, latitude: 26.1685 },
- { 土壤编号: "土29", 地点: "祁阳县中国农业科学院红壤实验室", dust_emissions: 4.75, longitude: 111.4, latitude: 22.24 },
- { 土壤编号: "土30", 地点: "福建福州1", dust_emissions: 4.31, longitude: 112.4, latitude: 23.24 },
- { 土壤编号: "土31", 地点: "福建福州2", dust_emissions: 4.38, longitude: 113.4, latitude: 24.24 },
- { 土壤编号: "土32", 地点: "广东省韶关市南雄市下塅村", dust_emissions: 5.51, longitude: 114.4, latitude: 25.24 },
- { 土壤编号: "土33", 地点: "广东省韶关市南雄市河塘西216米", dust_emissions: 6.44, longitude: 114.28, latitude: 25.14 },
- { 土壤编号: "土34", 地点: "广东省韶关市南雄市上何屋西南500米", dust_emissions: 5.25, longitude: 114.15, latitude: 24.86 },
- { 土壤编号: "土35", 地点: "广东省南雄市雄州街道林屋", dust_emissions: 4.62333333333333, longitude: 114.23, latitude: 25.4 },
- { 土壤编号: "土36", 地点: "广东省台山都斛镇", dust_emissions: 3.0, longitude: 112.34, latitude: 27.31 },
- { 土壤编号: "土52", 地点: "湖南省长沙市浏阳市永安镇千鹭湖", dust_emissions: 4.72333333333333, longitude: 113.34, latitude: 28.31 },
- { 土壤编号: "土53", 地点: "湖南省长沙市浏阳市湖南农大实习基地", dust_emissions: 5.55333333333333, longitude: 113.83, latitude: 28.3 },
- { 土壤编号: "土54", 地点: "湖南省邵阳市罗市镇1", dust_emissions: 4.64, longitude: 110.35, latitude: 25.47 },
- { 土壤编号: "土55", 地点: "湖南省邵阳市罗市镇2", dust_emissions: 5.01333333333333, longitude: 111.35, latitude: 26.47 },
- { 土壤编号: "土56", 地点: "湖南省邵阳市罗市镇3", dust_emissions: 5.18, longitude: 112.35, latitude: 27.47 },
- { 土壤编号: "土57", 地点: "长沙县高桥镇的省农科院高桥科研基地1", dust_emissions: 5.1, longitude: 113.35, latitude: 28.47 },
- { 土壤编号: "土58", 地点: "长沙县高桥镇的省农科院高桥科研基地2", dust_emissions: 4.92, longitude: 113.35, latitude: 28.47 },
- { 土壤编号: "土59", 地点: "湖南省长沙市望城区桐林坳社区", dust_emissions: 3.0, longitude: 112.8, latitude: 28.37 },
- { 土壤编号: "土60", 地点: "湖南省益阳市赫山区泥江口镇", dust_emissions: 3.0, longitude: 107.37, latitude: 21.92 },
- { 土壤编号: "土70", 地点: "南宁市兴宁区柳杨路26号", dust_emissions: 3.0, longitude: 108.37, latitude: 22.92 },
- { 土壤编号: "土71", 地点: "南宁市兴宁区柳杨路广西私享家家具用品", dust_emissions: 3.0, longitude: 108.37, latitude: 23.94 },
- { 土壤编号: "土72", 地点: "南宁市兴宁区004乡道", dust_emissions: 6.24666666666667, longitude: 108.39, latitude: 24.92 },
- { 土壤编号: "土73", 地点: "南宁市兴宁区G7201南宁绕城高速", dust_emissions: 3.0, longitude: 108.4, latitude: 25.94 },
- { 土壤编号: "土74", 地点: "南宁市兴宁区012县道", dust_emissions: 3.0, longitude: 108.41, latitude: 26.92 },
- { 土壤编号: "土75", 地点: "南宁市兴宁区那况路168号", dust_emissions: 3.0, longitude: 108.4, latitude: 27.9 },
- { 土壤编号: "土76", 地点: "南宁市西乡塘区翊武路", dust_emissions: 5.37, longitude: 108.35, latitude: 28.96 },
- { 土壤编号: "土77", 地点: "南宁市西乡塘区坛洛镇", dust_emissions: 3.0, longitude: 107.85, latitude: 29.92 },
- { 土壤编号: "土81", 地点: "铜仁职业技术学院", dust_emissions: 4.0, longitude: 108.85, latitude: 27.34 },
- { 土壤编号: "土87", 地点: "江西省红壤及种质资源研究所(进贤基地)1", dust_emissions: 4.55, longitude: 116.17, latitude: 28.34 },
- { 土壤编号: "土88", 地点: "江西省红壤及种质资源研究所(进贤基地)2", dust_emissions: 4.99333333333333, longitude: 116.17, latitude: 28.34 }
- ].map(item => {
- const lat = Number(item.latitude);
- const lng = Number(item.longitude);
-
- if (isNaN(lat) || isNaN(lng)) {
- console.error('无效的经纬度数据:', item);
- return null;
- }
-
- return {
- ...item,
- latitude: lat,
- longitude: lng
- };
- }).filter(item => item !== null);
- }
- // 初始化地图
- const initMap = async () => {
- try {
- await loadSDK()
-
- map = new TMap.value.Map(mapContainer.value, {
- center: new TMap.value.LatLng(24.81088,113.59762),
- zoom: 12,
- renderOptions: {
- preserveDrawingBuffer: true, // 必须开启以支持截图
- antialias: true
- }
- })
- // const defaultStyle = new TMap.value.MarkerStyle({
- // width: 34,
- // height: 34,
- // anchor: { x: 17, y: 34 },
- // src: markerIcon
- // })
-
- // markersLayer = new TMap.value.MultiMarker({
- // map: map,
- // styles: { default: defaultStyle }
- // })
- const geojsonData = await loadGeoJSON('/data/单元格.geojson');
- initMapWithGeoJSON(geojsonData, map);
- await initSurveyDataLayer(map);
- // 绑定点击事件
- // map.on('click', handleMapClick)
- // markersLayer.on('click', handleMarkerClick)
- // 新增地图就绪状态监听
- map.on('idle', () => {
- isMapReady.value = true
- })
- loadData()
- updateMarkers()
- } catch (err) {
- error.value = err.message
- }
- }
- // 加载数据并创建标记
- const loadData = () => {
- const geometries = state.excelData.map(item => ({
- id: item.土壤编号,
- styleId: 'default',
- position: new TMap.value.LatLng(item.latitude, item.longitude),
- properties: {
- title: item.地点,
- phValue: parseFloat(item.dust_emissions).toFixed(2),
- isTemp: false
- },
- }))
- markersLayer.setGeometries(geometries)
- }
- // 新增截图方法
- const exportMapImage = async () => {
- try {
- isExporting.value = true
-
- // 等待地图稳定
- await new Promise(resolve => setTimeout(resolve, 300))
- const canvas = await html2canvas(mapContainer.value, {
- useCORS: true,
- scale: window.devicePixelRatio || 2,
- backgroundColor: null,
- logging: true,
- onclone: (clonedDoc) => {
- // 处理控件可见性
- clonedDoc.querySelectorAll('.tmap-control').forEach(control => {
- control.style.visibility = exportSettings.showMapControls ? 'visible' : 'hidden'
- })
-
- // 添加水印
- if(exportSettings.showWatermark){
- const watermark = document.createElement('div')
- watermark.style = `
- position: absolute;
- bottom: 20px;
- right: 20px;
- color: rgba(0,0,0,0.2);
- font-size: 24px;
- transform: rotate(-15deg);
- z-index: 9999;
- `
- watermark.textContent = '机密地图 - 禁止外传'
- clonedDoc.body.appendChild(watermark)
- }
- }
- })
- // 转换为Blob
- canvas.toBlob(blob => {
- const link = document.createElement('a')
- link.download = `土壤地图_${new Date().toISOString().slice(0,10)}.png`
- link.href = URL.createObjectURL(blob)
- link.click()
- URL.revokeObjectURL(link.href)
- }, 'image/png', exportSettings.quality)
- } catch (error) {
- console.error('截图失败:', error)
- error.value = '截图失败,请尝试缩小地图层级'
- setTimeout(() => error.value = null, 3000)
- } finally {
- isExporting.value = false
- }
- }
- // 更新标记
- const updateMarkers = () => {
- const markers = state.excelData.map((item, index) => ({
- id: `marker-${index + 1}`,
- styleId: 'default',
- position: new TMap.value.LatLng(item.latitude, item.longitude),
- properties: {
- title: item.地点,
- phValue: item.dust_emissions,
- isTemp: false
- },
- }))
- markersLayer.setGeometries(markers)
- }
- // 新增Marker点击事件处理
- const handleMarkerClick = (e) => {
- const marker = e.geometry
- if (!marker) return
- // 关闭之前的信息窗口
- if (activeMarker.value?.id === marker.id) {
- infoWindow.close()
- activeMarker.value = null
- return
- }
- // 创建信息窗口内容
- const content = `
- <div style="padding:12px">
- <h3>${marker.properties.title}</h3>
- <p>PH值: ${marker.properties.phValue}</p>
- </div>
- `
- // 打开信息窗口
- infoWindow = new TMap.value.InfoWindow({
- map: map,
- position: marker.position,
- content: content,
- offset: {x: 0, y: -32}
- })
- // 记录当前激活的Marker
- activeMarker.value = marker
- // 点击其他区域关闭窗口
- map.on('click', closeInfoWindow)
- }
- const manageTempMarker = {
- add: (lat, lng, phValue) => {
- if (activeTempMarker.value) {
- markersLayer.remove("-999")
- }
-
- const tempMarker = markersLayer.add({
- id: "-999",
- position: new TMap.value.LatLng(lat, lng),
- styleId: 'temp',
- properties: {
- title: '克里金插值',
- phValue: parseFloat(phValue).toFixed(2),
- isTemp: true
- }
- })
- activeTempMarker.value = tempMarker
- },
- remove: () => {
- if (activeTempMarker.value) {
- markersLayer.remove("-999")
- activeTempMarker.value = null
- }
- }
- }
- // const handleMapClick = async (e) => {
- // if (selectedPolygon.value) {
- // resetPolygonStyle();
- // infoWindow.value?.close();
- // }
- // const now = Date.now()
-
- // if (now - state.lastTapTime < 1000) return
- // state.lastTapTime = now
- // try {
- // const latLng = e?.latLng
- // if (!latLng) throw new Error("地图点击事件缺少坐标信息")
- // const lat = Number(latLng.lat)
- // const lng = Number(latLng.lng)
- // if (!isValidCoordinate(lat, lng)) throw new Error(`非法坐标值 (${lat}, ${lng})`)
- // console.log('有效坐标:', lat, lng)
- // const result = await reverseGeocode(lat, lng)
- // if (!validateLocation(result)) throw new Error('非有效陆地区域')
- // const phValue = await getPhValue(lng, lat)
- // // 使用封装方法添加临时标记
- // manageTempMarker.add(lat, lng, phValue)
- // if (infoWindow.value) {
- // infoWindow.value.close()
- // }
- // infoWindow.value = new TMap.value.InfoWindow({
- // map: map,
- // position: manageTempMarker.activeTempMarker.value.getPosition(),
- // content: `
- // <div style="padding:12px">
- // <h3>${manageTempMarker.activeTempMarker.value.properties.title}</h3>
- // <p>PH值: ${manageTempMarker.activeTempMarker.value.properties.phValue}</p>
- // </div>
- // `
- // })
- // infoWindow.value.open()
- // } catch (error) {
- // console.error('操作失败详情:', error)
- // error.value = error.message.includes('非法坐标')
- // ? '请点击有效地图区域'
- // : '服务暂时不可用,请稍后重试'
- // setTimeout(() => error.value = null, 3000)
- // }
- // }
- // // 关闭信息窗口时同步移除临时标记
- // const closeInfoWindow = () => {
- // if (activeTempMarker.value) {
- // manageTempMarker.remove()
- // }
- // if (infoWindow.value) {
- // infoWindow.value.close()
- // infoWindow.value = null
- // }
- // map.off('click', closeInfoWindow)
- // }
- // // 验证坐标有效性
- // const isValidCoordinate = (lat, lng) => {
- // return !isNaN(lat) && !isNaN(lng) &&
- // lat >= -90 && lat <= 90 &&
- // lng >= -180 && lng <= 180
- // }
- // // 逆地理编码
- // const reverseGeocode = (lat, lng) => {
- // return new Promise((resolve, reject) => {
- // const callbackName = `tmap_callback_${Date.now()}`
- // window[callbackName] = (response) => {
- // delete window[callbackName]
- // document.body.removeChild(script)
- // if (response.status !== 0) reject(response.message)
- // else resolve(response.result)
- // }
- // const script = document.createElement('script')
- // script.src = `https://apis.map.qq.com/ws/geocoder/v1/?location=${lat},${lng}&key=${tMapConfig.key}&output=jsonp&callback=${callbackName}`
- // script.onerror = reject
- // document.body.appendChild(script)
- // })
- // }
- // // 验证地理位置
- // const validateLocation = (result) => {
- // if (!result || !result.address_component) {
- // return false;
- // }
- // return result.address_component.nation === '中国' &&
- // !['香港特别行政区', '澳门特别行政区', '台湾省'].includes(
- // result.address_component.province
- // )
- // }
- // // 获取PH值
- // const getPhValue = async (lng, lat) => {
- // try {
- // const { data } = await axios.post('https://soilgd.com:5000/kriging_interpolation', {
- // file_name: 'emissions.xlsx',
- // emission_column: 'dust_emissions',
- // points: [[lng, lat]]
- // })
- // return parseFloat(data.interpolated_concentrations[0]).toFixed(2)
- // } catch (error) {
- // console.error('获取PH值失败:', error)
- // throw error
- // }
- // }
- async function loadGeoJSON(url) {
- const response = await fetch(url);
- return await response.json();
- }
- function initMapWithGeoJSON(geojsonData, map) {
- // 销毁旧图层
- if (geoJSONLayer) {
- geoJSONLayer.setMap(null);
- geoJSONLayer = null;
- }
- // 创建 GeoJSONLayer
- geoJSONLayer = new TMap.value.vector.GeoJSONLayer({
- map: map,
- data: geojsonData,
- zIndex: 1,
- polygonStyle: new TMap.value.PolygonStyle({ // 必须用 PolygonStyle 类实例
- color: 'rgba(255, 0, 0, 0.25)',
- showBorder: true,
- borderColor: '#FF0000',
- borderWidth: 2
- })
- });
- // 获取多边形覆盖层
- multiPolygon = geoJSONLayer.getGeometryOverlay('polygon');
- // 高亮选中图层
- const highlightLayer = new TMap.value.MultiPolygon({
- map,
- zIndex: 2,
- styles: {
- highlight: new TMap.value.PolygonStyle({ // 注意要改为 PolygonStyle
- color: 'rgba(0, 123, 255, 0.5)', // 半透明蓝色填充
- borderColor: '#00FF00', // 荧光绿边框
- borderWidth: 2, // 加粗边框
- showBorder: true,
- extrudeHeight: 15 // 3D拔起效果[1](@ref)
- })
- }});
- // 高亮区域
- let highlightGeometry = {
- id: 'highlightGeo',
- styleId: 'highlight'
- }
- // 绑定点击事件(替换原有的事件监听)
- multiPolygon.on('hover', (e) => {
- if (e.geometry) {
- // 鼠标选中时高亮区域覆盖
- highlightGeometry.paths = e.geometry.paths;
- highlightLayer.updateGeometries([highlightGeometry]);
- } else {
- // 鼠标移出时取消高亮区域覆盖
- highlightLayer.setGeometries([]);
- }
- })
- };
- // 加载调查数据并初始化图层
- const initSurveyDataLayer = async (map) => {
- try {
- // 加载GeoJSON数据
- const surveyData = await loadGeoJSON('/data/调查数据.geojson');
-
- // 创建分类样式
- const pointStyles = Object.keys(categoryColors).map(category => ({
- id: category,
- style: new TMap.value.MarkerStyle({
- width: 12,
- height: 12,
- anchor: { x: 6, y: 6 },
- src: createColoredCircle(categoryColors[category]) // 生成圆形图标
- })
- }));
- // 初始化图层
- surveyDataLayer = new TMap.value.MultiMarker({
- map: map,
- styles: Object.assign({}, ...pointStyles.map(s => ({ [s.id]: s.style }))),
- geometries: surveyData.features.map(feature => ({
- id: feature.properties.ID,
- styleId: feature.properties.H_XTFX,
- position: new TMap.value.LatLng(
- feature.geometry.coordinates[1],
- feature.geometry.coordinates[0]
- ),
- properties: {
- ...feature.properties,
-
- }
- }))
- });
- // 添加点击事件
- surveyDataLayer.on('click', (event) => {
- const prop = event.geometry.properties;
- if (currentInfoWindow) currentInfoWindow.close();
- currentInfoWindow = new TMap.value.InfoWindow({
- map: map,
- position: event.geometry.position,
- content: `
- <div class="point-info">
- <h3>${prop.XMC} ${prop.ZMC} ${prop.CMC}</h3>
- <p>${prop.H_XTFX}</p>
- </div>
- `
- });
- });
- } catch (error) {
- console.error('调查数据加载失败:', error);
- }
- };
- // 生成圆形图标的base64数据
- const createColoredCircle = (color) => {
- const canvas = document.createElement('canvas');
- canvas.width = 24;
- canvas.height = 24;
- const ctx = canvas.getContext('2d');
-
- // 绘制圆形
- ctx.beginPath();
- ctx.arc(12, 12, 8, 0, 2 * Math.PI);
- ctx.fillStyle = color;
- ctx.fill();
-
- // 添加白色边框
- ctx.strokeStyle = 'black';
- ctx.lineWidth = 2;
- ctx.stroke();
-
- return canvas.toDataURL();
- };
- const toggleSoilTypeLayer = () => {
- if (!multiPolygon) {
- console.error('利用类型图层未初始化');
- return;
- }
- if (multiPolygon) {
- multiPolygon.setVisible(state.showSoilTypes);
- }
- };
- const toggleSurveyDataLayer = () => {
- if (!surveyDataLayer) {
- console.error('调查数据图层未初始化');
- return;
- }
- if (surveyDataLayer) {
- surveyDataLayer.setVisible(state.showSurveyData);
- }
- };
- // // 切换覆盖层
- // const toggleOverlay = () => {
- // if (state.showOverlay) {
- // overlay = new TMap.value.ImageGroundLayer({
- // map: map,
- // bounds: new TMap.value.LatLngBounds(
- // new TMap.value.LatLng(18.17, 103.55),
- // new TMap.value.LatLng(32.32, 119.82)
- // ),
- // src: 'https://soilgd.com/images/farmland_cut.png'
- // })
- // } else {
- // if (overlay) {
- // overlay.setMap(null)
- // overlay = null
- // }
- // }
- // }
- onMounted(async () => {
- try {
- await loadSDK()
- initData()
- await initMap()
- } catch (err) {
- error.value = err.message
- }
- })
- onBeforeUnmount(() => {
- if (activeTempMarker.value) {
- manageTempMarker.remove()
- }
- if (markersLayer) markersLayer.setMap(null)
- if (overlay) overlay.setMap(null)
- if (infoWindow.value) {
- infoWindow.value.close()
- infoWindow.value = null
- }
- if (farmlandLayer) {
- farmlandLayer.destroy();
- farmlandLayer = null;
- }
- if (bboxLayer) {
- bboxLayer.destroy();
- bboxLayer = null;
- }
- if (soilTypeLayer) {
- soilTypeLayer.destroy();
- soilTypeLayer = null;
- }
- if (surveyDataLayer) {
- surveyDataLayer.destroy();
- surveyDataLayer = null;
- }
- })
- </script>
- <style scoped>
- .map-page {
- position: relative;
- width: 100vw;
- height: 100vh;
- }
- .map-container {
- width: 100%;
- height: 100vh !important;
- min-height: 600px;
- }
- .control-panel {
- position: fixed;
- top: 24px;
- right: 24px;
- background: rgba(255, 255, 255, 0.95);
- padding: 16px;
- border-radius: 12px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
- backdrop-filter: blur(8px);
- border: 1px solid rgba(255, 255, 255, 0.2);
- z-index: 1000;
- min-width: 240px;
- transition: all 0.3s ease;
- }
- .control-panel:hover {
- box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
- transform: translateY(-2px);
- }
- .control-panel label {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 8px 12px;
- border-radius: 8px;
- transition: background 0.2s ease;
- cursor: pointer;
- }
- .control-panel label:hover {
- background: rgba(56, 118, 255, 0.05);
- }
- .control-panel input[type="checkbox"] {
- width: 18px;
- height: 18px;
- border: 2px solid #3876ff;
- border-radius: 4px;
- appearance: none;
- cursor: pointer;
- transition: all 0.2s ease;
- }
- .control-panel input[type="checkbox"]:checked {
- background: #3876ff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24'%3E%3Cpath fill='%23fff' d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E") no-repeat center;
- background-size: 12px;
- }
- .export-controls {
- display: flex;
- flex-direction: column;
- gap: 12px;
- margin-top: 16px;
- }
- .export-controls button {
- padding: 10px 16px;
- font-size: 14px;
- font-weight: 500;
- border: none;
- border-radius: 8px;
- cursor: pointer;
- transition: all 0.2s ease;
- display: flex;
- align-items: center;
- gap: 8px;
- background: #3876ff;
- color: white;
- }
- .export-controls button:disabled {
- background: #e0e0e0;
- color: #9e9e9e;
- cursor: not-allowed;
- opacity: 0.8;
- }
- .export-controls button:not(:disabled):hover {
- background: #2b5dc5;
- box-shadow: 0 4px 12px rgba(56, 118, 255, 0.3);
- }
- /* 新增加载动画 */
- @keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- .loading-spinner {
- width: 18px;
- height: 18px;
- border: 2px solid rgba(255, 255, 255, 0.3);
- border-top-color: white;
- border-radius: 50%;
- animation: spin 0.8s linear infinite;
- }
- /* 响应式调整 */
- @media (max-width: 768px) {
- .control-panel {
- top: 16px;
- right: 16px;
- left: 16px;
- width: auto;
- min-width: auto;
- }
-
- .export-controls {
- flex-direction: row;
- flex-wrap: wrap;
- }
-
- .export-controls button {
- flex: 1;
- justify-content: center;
- }
- }
- /* 信息窗口样式 */
- :deep(.tmap-infowindow) {
- padding: 12px;
- min-width: 200px;
- border-radius: 8px;
- box-shadow: 0 2px 8px rgba(0,0,0,0.15);
- background-color: white;
- }
- :deep(.tmap-infowindow h3) {
- margin: 0 0 8px;
- font-size: 16px;
- color: #333;
- }
- :deep(.tmap-infowindow p) {
- margin: 4px 0;
- color: #666;
- font-size: 14px;
- }
- .polygon-info {
- padding: 12px;
- max-width: 300px;
-
- h3 {
- margin: 0 0 8px;
- color: #333;
- font-size: 16px;
- }
- table {
- width: 100%;
- border-collapse: collapse;
- tr {
- border-bottom: 1px solid #eee;
- }
- th, td {
- padding: 6px 4px;
- text-align: left;
- font-size: 14px;
- }
- th {
- color: #666;
- white-space: nowrap;
- padding-right: 8px;
- }
- }
- }
- .point-info {
- padding: 12px;
- min-width: 200px;
-
- h3 {
- margin: 0 0 8px;
- font-size: 14px;
- color: white;
- padding: 4px 8px;
- border-radius: 4px;
- display: inline-block;
- background: var(--category-color);
- }
-
- p {
- margin: 6px 0;
- font-size: 13px;
- line-height: 1.4;
-
- &:last-child {
- margin-bottom: 0;
- }
- }
- }
- /* 动态类别颜色 */
- .point-info h3[data-category="优先保护类"] { --category-color: #00C853; }
- .point-info h3[data-category="安全利用类"] { --category-color: #FFD600; }
- .point-info h3[data-category="严格管控类"] { --category-color: #D50000; }
- .highlight-status {
- padding: 8px;
- background: rgba(0, 255, 0, 0.1);
- border-left: 3px solid #00FF00;
- margin-top: 12px;
- }
- </style>
|