vue3 +天地图

引言

天地图API

vue-tianditu | VueTianditu

安装依赖

pnpm add vue-tianditu

mian.ts引入

import VueTianditu from 'vue-tianditu'
import "vue-tianditu/lib/style.css";


const app = createApp(App)
app.use(VueTianditu,{
    v: "4.0",
    tk: "59c74f20e811115621a8694b9a3737773a70"
})
app.mount('#app')

index.vue

<template>
    <div class="app">
      <div class="btn-group">
        <el-button @click="router.push('/demo4')">地图控件</el-button>
        <el-button @click="router.push('/demo2')">地图描边/区/搜索</el-button>
        <el-button @click="router.push('/demo3')">地图区域点击高亮</el-button>
        <el-button @click="router.push('/demo5')">地图工具</el-button>
        <el-button @click="router.push('/demo6')">地图密集点/区域高亮/边界高亮</el-button>
      </div>
      <div class="main">
        <router-view></router-view>
      </div>
    </div>
  </template>
  
  <script setup lang="ts">
  import { ref } from 'vue';
  import { useRouter} from 'vue-router'
  const router = useRouter()
  </script>
  <style scoped lang="scss">
  .app{
    width: 100%;
    height: 100vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    .main{
      flex:1;
    }
  }
  </style>
  
  

demo4.vue

<template>
  <hr></hr>
  <button @click="state.visible = !state.visible">显示自定义控件:{{ state.visible }}</button>
  <button @click="state.copyright = !state.copyright">显示默认版权控件:{{ state.copyright }}</button>
  <div class="mapDiv" :class="state.copyright ? '' : 'hide-copyright'">
    <tdt-map :center="state.center" :zoom="state.zoom" :controls="state.controls">
      <tdt-control position="topright" :visible="state.visible">
        <button>自定义控件</button>
      </tdt-control>
    </tdt-map>
  </div>
</template>

<script lang="ts" setup>
import { reactive } from "vue";

const state = reactive({
  center: [113.280637, 23.125178],
  zoom: 11,
  controls: [
    "Zoom",
    "Scale",
    {
      name: "MapType",
      position: "topright",
      mapTypes: [
        {
          title: "地图", //地图控件上所要显示的图层名称
          icon: "http://api.tianditu.gov.cn/v4.0/image/map/maptype/vector.png", //地图控件上所要显示的图层图标(默认图标大小 80x80)
          layer: "TMAP_NORMAL_MAP" //地图类型,在原天地图api中以window.TMAP_NORMAL_MAP表示,此处为字符串
        },
        {
          title: "卫星",
          icon: " http://api.tianditu.gov.cn/v4.0/image/map/maptype/satellite.png",
          layer: "TMAP_SATELLITE_MAP"
        },
        {
          title: "卫星混合",
          icon: "http://api.tianditu.gov.cn/v4.0/image/map/maptype/satellitepoi.png",
          layer: "TMAP_HYBRID_MAP"
        }
        // {
        //   title: "地形",
        //   icon: "http://api.tianditu.gov.cn/v4.0/image/map/maptype/terrain.png",
        //   layer: "TMAP_TERRAIN_MAP"
        // },
        // {
        //   title: "地形混合",
        //   icon: "http://api.tianditu.gov.cn/v4.0/image/map/maptype/terrainpoi.png",
        //   layer: "TMAP_TERRAIN_HYBRID_MAP"
        // }
      ]
    },
    {
      name: "OverviewMap",
      isOpen: true,
      anchor: "bottomright"
    },
    {
      name: "Copyright",
      id: "custom",
      content: `<div style="height:40px"><button>自定义的版权控件</button></div>`,
      position: "bottomleft",
      bounds: [
        [113.52791, 23.21989],
        [113.03352, 23.03045]
      ]
    }
  ],
  visible: true,
  copyright: true
});
</script>

<script lang="ts">
export default { name: "demo-control" };
</script>

<style scoped>
.mapDiv {
  width: 100%;
  height: 80vh;
}
::v-deep.hide-copyright .tdt-control-copyright.tdt-control > div:not(.tdt-control-copyright) {
  display: none;
}
</style>

demo2.vue

<template>
    <div class="mapDiv">
        <el-button @click="drawChengduBoundary('city')">成都市</el-button>
        <el-button @click="drawChengduBoundary('xindu')">新都区</el-button>
        <tdt-map :center="state.center" :zoom="state.zoom" :mapStyle="state.mapStyle" @init="onInit">
            <tdt-tilelayer-tdt :url="state.url" :zIndex="1"></tdt-tilelayer-tdt>
            <tdt-search @search-complete="searchComplete" @poi-click="poiClick" @suggest-click="suggestClick"></tdt-search>
        </tdt-map>
    </div>
</template>

<script lang="ts" setup>
import { reactive, ref, onMounted } from "vue";
const TIANDITU_KEY = "11111"
import "vue-tianditu/lib/style.css";
import chengduJson from './chengdu.json'
import xinduJson from './xindu.json'


const state = reactive({
    center: [104.0657, 30.6594], //地图中心
    zoom: 12, // 地图缩放级别
    mapStyle: 'black', // 地图样式
    url: "http://t0.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=7f013d0186775b063d6a046977bbefc6"
});


const mapInstance = ref<any>(null);  // 地图实例
const chengduPolygon = ref<any>(null); // 成都市边界
const allPolygons = ref<any[]>([]); // 跟踪所有创建的多边形
const highlightedPolygon = ref<any>(null); // 当前高亮的多边形
const originalStyles = ref<Map<any, any>>(new Map()); // 保存原始样式
const allLabels = ref<any[]>([]); // 跟踪所有创建的标签

const onInit = (map: any) => {
    mapInstance.value = map;
}

function searchComplete(result: T.LocalSearchResult) {
  console.log(result);
}
function poiClick(poi: T.LocalSearchPoi) {
  console.log(poi);
}
function suggestClick(suggest: T.LocalSearchSuggest) {
  console.log(suggest);
}

// 绘制成都市边界
function drawChengduBoundary(type:string) {
    console.log('开始绘制成都市边界...');
    console.log('当前地图实例状态:', mapInstance.value);
    const mapJson = type === 'city' ? chengduJson : xinduJson;

    if (!mapInstance.value) {
        console.error('地图实例未初始化,请等待地图加载完成');
        // 尝试延迟执行
        setTimeout(() => {
            if (mapInstance.value) {
                console.log('延迟执行:地图实例已就绪,重新绘制...');
                drawChengduBoundary(type);
            } else {
                console.error('延迟执行失败:地图实例仍未就绪');
            }
        }, 1000);
        return;
    }

    // 清除之前的边界
    if (chengduPolygon.value) {
        console.log('清除之前的边界...');
        mapInstance.value.removeOverLay(chengduPolygon.value);
    }

    // 清除之前的跟踪数组
    if (allPolygons.value.length > 0) {
        console.log('清除之前的跟踪数组...');
        allPolygons.value.forEach((polygon: any) => {
            if (polygon && mapInstance.value) {
                mapInstance.value.removeOverLay(polygon);
            }
        });
        allPolygons.value = [];
    }

    // 清除高亮状态
    highlightedPolygon.value = null;
    originalStyles.value.clear();

    try {
        console.log('解析GeoJSON数据...');

        // 解析GeoJSON数据,提取坐标点和属性信息
        let allCoordinates: number[][][] = [];
        let allProperties: any[] = [];

        if (mapJson.features && mapJson.features.length > 0) {
            // 遍历所有要素,收集所有多边形的坐标和属性
            mapJson.features.forEach((feature: any) => {
                if (feature.geometry && feature.geometry.coordinates) {
                    if (feature.geometry.type === 'Polygon') {
                        // 单个多边形
                        allCoordinates.push(feature.geometry.coordinates[0]);
                        allProperties.push(feature.properties);
                    } else if (feature.geometry.type === 'MultiPolygon') {
                        // 多个多边形
                        feature.geometry.coordinates.forEach((polygon: number[][][]) => {
                            allCoordinates.push(polygon[0]); // 取每个多边形的外环
                            allProperties.push(feature.properties);
                        });
                    }
                }
            });
        }

        if (allCoordinates.length === 0) {
            console.error('无法从GeoJSON中提取坐标数据');
            return;
        }

        console.log('提取的坐标数据:', allCoordinates);
        console.log('提取的属性数据:', allProperties);

        // 为每个多边形创建边界和标签
        allCoordinates.forEach((coordinates: number[][], index: number) => {
            if (coordinates.length > 0) {
                // 创建多边形边界 - 使用正确的天地图API
                const points = coordinates.map(coord => new (window as any).T.LngLat(coord[0], coord[1]));
                console.log(`创建第${index + 1}个多边形,坐标点数量:`, points.length);

                // 创建多边形 - 使用正确的构造函数
                const polygon = new (window as any).T.Polygon(points, {
                    color: '#FF0000',        // 红色边框
                    weight: 2,               // 边框宽度
                    opacity: 1,              // 边框透明度
                    fillColor: '#FF0000',    // 填充颜色
                    fillOpacity: 0.1         // 填充透明度
                });

                // 添加唯一标识符
                polygon._uniqueId = `polygon_${index}`;
                console.log(`多边形 ${polygon._uniqueId} 创建完成`);

                // 保存原始样式
                originalStyles.value.set(polygon, {
                    color: '#FF0000',
                    weight: 2,
                    opacity: 1,
                    fillColor: '#FF0000',
                    fillOpacity: 0.1
                });

               

                // 添加鼠标悬停事件
                polygon.addEventListener('mouseover', () => {
                    if (highlightedPolygon.value !== polygon) {
                        polygon.setOptions({
                            color: '#FFA500',        // 橙色边框
                            weight: 3,               // 加粗边框
                            fillOpacity: 0.2         // 增加填充透明度
                        });
                    }
                });

                polygon.addEventListener('mouseout', () => {
                    if (highlightedPolygon.value !== polygon) {
                        // 恢复原始样式
                        const originalStyle = originalStyles.value.get(polygon);
                        if (originalStyle) {
                            polygon.setOptions(originalStyle);
                        }
                    }
                });

                // 添加到地图
                mapInstance.value.addOverLay(polygon);

                // 保存到跟踪数组
                allPolygons.value.push(polygon);

                // 保存第一个多边形用于清除功能
                if (index === 0) {
                    chengduPolygon.value = polygon;
                }

                // 创建区域名称标签
                const properties = allProperties[index];
                if (properties && properties.name) {
                    // 使用centroid坐标作为标签位置,如果没有则计算多边形中心
                    let labelPosition;
                    if (properties.centroid && properties.centroid.length === 2) {
                        labelPosition = new (window as any).T.LngLat(properties.centroid[0], properties.centroid[1]);
                    } else {
                        // 计算多边形中心点
                        const centerLng = coordinates.reduce((sum, coord) => sum + coord[0], 0) / coordinates.length;
                        const centerLat = coordinates.reduce((sum, coord) => sum + coord[1], 0) / coordinates.length;
                        labelPosition = new (window as any).T.LngLat(centerLng, centerLat);
                    }

                    console.log(`准备创建标签: ${properties.name}, 位置:`, labelPosition);

                    // 尝试多种方式创建标签
                    let label;
                    try {
                        // 方式1:使用天地图的标准Label构造函数  这个方法背景没有被修改
                        throw new Error('方式1失败,尝试方式2');
                        // if ((window as any).T && (window as any).T.Label) {
                        //     label = new (window as any).T.Label({
                        //         text: properties.name,
                        //         position: labelPosition,
                        //         backgroundColor: 'transparent',
                        //     });
                        //     console.log('方式1成功:使用标准Label构造函数');
                        // }
                    } catch (error) {
                        console.log('方式1失败,尝试方式2:', error);
                        
                        try {
                            // 方式2:使用HTML元素创建自定义标签
                            const labelDiv = document.createElement('div');
                            labelDiv.innerHTML = properties.name;
                            labelDiv.style.cssText = `
                                position: absolute;
                                color: #FFFFFF;
                                fontSize: 14px;
                                fontWeight: bold;
                                backgroundColor: transparent;
                                padding: 4px 8px;
                                borderRadius: 4px;
                                border: none;
                                pointer-events: none;
                                z-index: 1000;
                                text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
                            `;
                            
                            // 创建标记点来承载标签
                            label = new (window as any).T.Marker(labelPosition, {
                                icon: new (window as any).T.Icon({
                                    iconUrl: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB2aWV3Qm94PSIwIDAgMSAxIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9Im5vbmUiLz48L3N2Zz4=',
                                    iconSize: [1, 1],
                                    iconAnchor: [0.5, 0.5]
                                })
                            });
                            
                            // 将HTML标签添加到地图容器
                            const mapContainer = mapInstance.value.getContainer();
                            if (mapContainer) {
                                mapContainer.appendChild(labelDiv);
                                
                                // 监听地图移动事件,更新标签位置
                                const updateLabelPosition = () => {
                                    const pixel = mapInstance.value.lngLatToContainerPoint(labelPosition);
                                    labelDiv.style.left = (pixel.x - labelDiv.offsetWidth / 2) + 'px';
                                    labelDiv.style.top = (pixel.y - labelDiv.offsetHeight / 2) + 'px';
                                };
                                
                                // 初始位置
                                updateLabelPosition();
                                
                                // 绑定地图事件
                                mapInstance.value.addEventListener('move', updateLabelPosition);
                                mapInstance.value.addEventListener('zoom', updateLabelPosition);
                                
                                // 保存更新函数引用,用于清理
                                label._updatePosition = updateLabelPosition;
                                label._labelDiv = labelDiv;
                            }
                            
                        } catch (error2) {
                            label = null;
                        }
                    }

                    if (label) {
                        // 添加唯一标识符
                        label._uniqueId = `label_${index}`;

                        // 添加到地图
                        mapInstance.value.addOverLay(label);

                        // 保存到标签跟踪数组
                        allLabels.value.push(label);
                    } else {
                        console.error(`无法创建标签: ${properties.name}`);
                    }
                }
            }
        });

        // 调整地图中心到整个区域
        console.log('调整地图中心到整个区域...');
        if (allCoordinates.length > 0) {
            // 计算所有坐标的边界
            let minLng = Infinity, maxLng = -Infinity;
            let minLat = Infinity, maxLat = -Infinity;

            allCoordinates.forEach(coordinates => {
                coordinates.forEach(coord => {
                    minLng = Math.min(minLng, coord[0]);
                    maxLng = Math.max(maxLng, coord[0]);
                    minLat = Math.min(minLat, coord[1]);
                    maxLat = Math.max(maxLat, coord[1]);
                });
            });

            // 计算中心点
            const centerLng = (minLng + maxLng) / 2;
            const centerLat = (minLat + maxLat) / 2;
            state.center = [centerLng, centerLat];
            state.zoom = 9; // 调整缩放级别以显示整个区域
            console.log('地图中心调整为:', state.center);
        }

        console.log('成都市边界绘制完成!');
    } catch (error) {
        console.error('绘制成都市边界失败:', error);
        console.error('错误详情:', {
            mapInstance: mapInstance.value,
            T: (window as any).T,
            mapJson: mapJson
        });
    }
}


// 地图初始化完成后获取地图实例
onMounted(() => {
    console.log('组件已挂载,等待天地图初始化...');
    // 现在主要通过@init事件获取地图实例,这里只作为备用方案
    setTimeout(() => {
        if (!mapInstance.value) {
            console.log('通过@init事件未获取到地图实例,尝试备用方案...');

            // 备用方案:检查全局天地图对象
            if ((window as any).T && (window as any).T.Map) {
                try {
                    const mapContainer = document.querySelector('.mapDiv');
                    if (mapContainer) {
                        const newMap = new (window as any).T.Map(mapContainer, {
                            projection: 'EPSG:4326'
                        });
                        mapInstance.value = newMap;
                        console.log('备用方案:通过全局T对象创建地图实例成功');
                    }
                } catch (error) {
                    console.log('备用方案创建地图实例失败:', error);
                }
            }
        }
    }, 5000); // 5秒后检查备用方案
});
</script>

<script lang="ts">
export default { name: "demo-mousetool" };
</script>

<style scoped>
.mapDiv {
    width: 100%;
    height: 80vh;
}
:deep(.tdt-search){
    top: 10px;
    right: 10px;
    position: absolute;
    z-index: 999;
}
</style>

demo3.vue

<template>
  <div class="mapDiv">
      <tdt-map :center="state.center" :zoom="state.zoom" :mapStyle="state.mapStyle" @init="onInit">
          <tdt-tilelayer-tdt :url="state.url" :zIndex="1"></tdt-tilelayer-tdt>
      </tdt-map>
  </div>
</template>

<script lang="ts" setup>
import { reactive, ref, onMounted } from "vue";
import chengduJson from "./chengdu.json"
const TIANDITU_KEY = "111"


const state = reactive({
  center: [104.0657, 30.6594], //地图中心
  zoom: 12, // 地图缩放级别
  mapStyle: 'black', // 地图样式
  url: "http://t0.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=7f013d0186775b063d6a046977bbefc6"
});


const mapInstance = ref<any>(null);  // 地图实例
const chengduPolygon = ref<any>(null); // 成都市边界
const allPolygons = ref<any[]>([]); // 跟踪所有创建的多边形
const highlightedPolygon = ref<any>(null); // 当前高亮的多边形
const originalStyles = ref<Map<any, any>>(new Map()); // 保存原始样式
const allLabels = ref<any[]>([]); // 跟踪所有创建的标签

const onInit = (map: any) => {
  mapInstance.value = map;
  drawChengduBoundary();
}



// 绘制成都市边界
function drawChengduBoundary() {
  console.log('开始绘制成都市边界...');
  console.log('当前地图实例状态:', mapInstance.value);
  console.log('成都数据:', chengduJson);

  if (!mapInstance.value) {
      console.error('地图实例未初始化,请等待地图加载完成');
      // 尝试延迟执行
      setTimeout(() => {
          if (mapInstance.value) {
              console.log('延迟执行:地图实例已就绪,重新绘制...');
              drawChengduBoundary();
          } else {
              console.error('延迟执行失败:地图实例仍未就绪');
          }
      }, 1000);
      return;
  }

  // 清除之前的边界
  if (chengduPolygon.value) {
      console.log('清除之前的边界...');
      mapInstance.value.removeOverLay(chengduPolygon.value);
  }

  // 清除之前的跟踪数组
  if (allPolygons.value.length > 0) {
      console.log('清除之前的跟踪数组...');
      allPolygons.value.forEach((polygon: any) => {
          if (polygon && mapInstance.value) {
              mapInstance.value.removeOverLay(polygon);
          }
      });
      allPolygons.value = [];
  }

  // 清除高亮状态
  highlightedPolygon.value = null;
  originalStyles.value.clear();

  try {
      console.log('解析GeoJSON数据...');

      // 解析GeoJSON数据,提取坐标点和属性信息
      let allCoordinates: number[][][] = [];
      let allProperties: any[] = [];

      if (chengduJson.features && chengduJson.features.length > 0) {
          // 遍历所有要素,收集所有多边形的坐标和属性
          chengduJson.features.forEach((feature: any) => {
              if (feature.geometry && feature.geometry.coordinates) {
                  if (feature.geometry.type === 'Polygon') {
                      // 单个多边形
                      allCoordinates.push(feature.geometry.coordinates[0]);
                      allProperties.push(feature.properties);
                  } else if (feature.geometry.type === 'MultiPolygon') {
                      // 多个多边形
                      feature.geometry.coordinates.forEach((polygon: number[][][]) => {
                          allCoordinates.push(polygon[0]); // 取每个多边形的外环
                          allProperties.push(feature.properties);
                      });
                  }
              }
          });
      }

      if (allCoordinates.length === 0) {
          console.error('无法从GeoJSON中提取坐标数据');
          return;
      }

      console.log('提取的坐标数据:', allCoordinates);
      console.log('提取的属性数据:', allProperties);

      // 为每个多边形创建边界和标签
      allCoordinates.forEach((coordinates: number[][], index: number) => {
          if (coordinates.length > 0) {
              // 创建多边形边界 - 使用正确的天地图API
              const points = coordinates.map(coord => new (window as any).T.LngLat(coord[0], coord[1]));
              console.log(`创建第${index + 1}个多边形,坐标点数量:`, points.length);

              // 创建多边形 - 使用正确的构造函数
              const polygon = new (window as any).T.Polygon(points, {
                  color: '#FF0000',        // 红色边框
                  weight: 2,               // 边框宽度
                  opacity: 1,              // 边框透明度
                  fillColor: '#FF0000',    // 填充颜色
                  fillOpacity: 0.1         // 填充透明度
              });

              // 添加唯一标识符
              polygon._uniqueId = `polygon_${index}`;
              console.log(`多边形 ${polygon._uniqueId} 创建完成`);

              // 保存原始样式
              originalStyles.value.set(polygon, {
                  color: '#FF0000',
                  weight: 2,
                  opacity: 1,
                  fillColor: '#FF0000',
                  fillOpacity: 0.1
              });

              // 添加点击事件 - 使用正确的事件绑定方式
              polygon.addEventListener('click', (e: any) => {
                  console.log('多边形点击事件触发');
                  // 阻止事件冒泡
                  if (e && e.originalEvent) {
                      e.originalEvent.stopPropagation();
                  }
                  handlePolygonClick(polygon, e);
              });

              // 添加鼠标悬停事件
              polygon.addEventListener('mouseover', () => {
                  if (highlightedPolygon.value !== polygon) {
                      polygon.setOptions({
                          color: '#FFA500',        // 橙色边框
                          weight: 3,               // 加粗边框
                          fillOpacity: 0.2         // 增加填充透明度
                      });
                  }
              });

              polygon.addEventListener('mouseout', () => {
                  if (highlightedPolygon.value !== polygon) {
                      // 恢复原始样式
                      const originalStyle = originalStyles.value.get(polygon);
                      if (originalStyle) {
                          polygon.setOptions(originalStyle);
                      }
                  }
              });

              // 添加到地图
              mapInstance.value.addOverLay(polygon);

              // 保存到跟踪数组
              allPolygons.value.push(polygon);

              // 保存第一个多边形用于清除功能
              if (index === 0) {
                  chengduPolygon.value = polygon;
              }

              // 创建区域名称标签
              const properties = allProperties[index];
              if (properties && properties.name) {
                  // 使用centroid坐标作为标签位置,如果没有则计算多边形中心
                  let labelPosition;
                  if (properties.centroid && properties.centroid.length === 2) {
                      labelPosition = new (window as any).T.LngLat(properties.centroid[0], properties.centroid[1]);
                  } else {
                      // 计算多边形中心点
                      const centerLng = coordinates.reduce((sum, coord) => sum + coord[0], 0) / coordinates.length;
                      const centerLat = coordinates.reduce((sum, coord) => sum + coord[1], 0) / coordinates.length;
                      labelPosition = new (window as any).T.LngLat(centerLng, centerLat);
                  }

                  console.log(`准备创建标签: ${properties.name}, 位置:`, labelPosition);

                  // 尝试多种方式创建标签
                  let label;
                  try {
                      // 方式1:使用天地图的标准Label构造函数  这个方法背景没有被修改
                      throw new Error('方式1失败,尝试方式2');
                      // if ((window as any).T && (window as any).T.Label) {
                      //     label = new (window as any).T.Label({
                      //         text: properties.name,
                      //         position: labelPosition,
                      //         backgroundColor: 'transparent',
                      //     });
                      //     console.log('方式1成功:使用标准Label构造函数');
                      // }
                  } catch (error) {
                      console.log('方式1失败,尝试方式2:', error);
                      
                      try {
                          // 方式2:使用HTML元素创建自定义标签
                          const labelDiv = document.createElement('div');
                          labelDiv.innerHTML = properties.name;
                          labelDiv.style.cssText = `
                              position: absolute;
                              color: #FFFFFF;
                              fontSize: 14px;
                              fontWeight: bold;
                              backgroundColor: transparent;
                              padding: 4px 8px;
                              borderRadius: 4px;
                              border: none;
                              pointer-events: none;
                              z-index: 1000;
                              text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
                          `;
                          
                          // 创建标记点来承载标签
                          label = new (window as any).T.Marker(labelPosition, {
                              icon: new (window as any).T.Icon({
                                  iconUrl: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB2aWV3Qm94PSIwIDAgMSAxIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9Im5vbmUiLz48L3N2Zz4=',
                                  iconSize: [1, 1],
                                  iconAnchor: [0.5, 0.5]
                              })
                          });
                          
                          // 将HTML标签添加到地图容器
                          const mapContainer = mapInstance.value.getContainer();
                          if (mapContainer) {
                              mapContainer.appendChild(labelDiv);
                              
                              // 监听地图移动事件,更新标签位置
                              const updateLabelPosition = () => {
                                  const pixel = mapInstance.value.lngLatToContainerPoint(labelPosition);
                                  labelDiv.style.left = (pixel.x - labelDiv.offsetWidth / 2) + 'px';
                                  labelDiv.style.top = (pixel.y - labelDiv.offsetHeight / 2) + 'px';
                              };
                              
                              // 初始位置
                              updateLabelPosition();
                              
                              // 绑定地图事件
                              mapInstance.value.addEventListener('move', updateLabelPosition);
                              mapInstance.value.addEventListener('zoom', updateLabelPosition);
                              
                              // 保存更新函数引用,用于清理
                              label._updatePosition = updateLabelPosition;
                              label._labelDiv = labelDiv;
                          }
                          
                      } catch (error2) {
                          label = null;
                      }
                  }

                  if (label) {
                      // 添加唯一标识符
                      label._uniqueId = `label_${index}`;

                      // 添加到地图
                      mapInstance.value.addOverLay(label);

                      // 保存到标签跟踪数组
                      allLabels.value.push(label);
                  } else {
                      console.error(`无法创建标签: ${properties.name}`);
                  }
              }
          }
      });

      // 调整地图中心到整个区域
      console.log('调整地图中心到整个区域...');
      if (allCoordinates.length > 0) {
          // 计算所有坐标的边界
          let minLng = Infinity, maxLng = -Infinity;
          let minLat = Infinity, maxLat = -Infinity;

          allCoordinates.forEach(coordinates => {
              coordinates.forEach(coord => {
                  minLng = Math.min(minLng, coord[0]);
                  maxLng = Math.max(maxLng, coord[0]);
                  minLat = Math.min(minLat, coord[1]);
                  maxLat = Math.max(maxLat, coord[1]);
              });
          });

          // 计算中心点
          const centerLng = (minLng + maxLng) / 2;
          const centerLat = (minLat + maxLat) / 2;
          state.center = [centerLng, centerLat];
          state.zoom = 9; // 调整缩放级别以显示整个区域
          console.log('地图中心调整为:', state.center);
      }

      console.log('成都市边界绘制完成!');
  } catch (error) {
      console.error('绘制成都市边界失败:', error);
      console.error('错误详情:', {
          mapInstance: mapInstance.value,
          T: (window as any).T,
          chengduJson: chengduJson
      });
  }
}

// 处理多边形点击事件
function handlePolygonClick(polygon: any, event: any) {
  console.log('🔍 多边形点击事件触发,当前高亮状态:', !!highlightedPolygon.value);
  
  // 如果点击的是当前高亮的多边形,则取消高亮
  if (highlightedPolygon.value === polygon) {
      console.log('✅ 检测到点击已高亮的多边形,执行取消高亮');
      cancelHighlight(polygon);
      return;
  }

  // 重要:如果之前有高亮的多边形,先取消它的高亮
  if (highlightedPolygon.value) {
      console.log('🔄 取消之前高亮的多边形');
      cancelHighlight(highlightedPolygon.value);
  }

  // 高亮当前多边形
  console.log('🎯 准备高亮当前多边形');
  highlightPolygon(polygon);
}

// 高亮多边形
function highlightPolygon(polygon: any) {
  if (!polygon) {
      console.error('❌ 多边形对象为空,无法高亮');
      return;
  }

  console.log('🎨 开始高亮多边形:', polygon._uniqueId || 'unknown');

  // 保存当前高亮的多边形
  highlightedPolygon.value = polygon;

  // 应用高亮样式 - 使用天地图的正确API
  try {
      // 使用天地图的正确方法设置样式
      if (typeof polygon.setColor === 'function') {
          polygon.setColor('#00FF00');        // 绿色边框
          console.log('✅ 设置边框颜色成功');
      }
      if (typeof polygon.setWeight === 'function') {
          polygon.setWeight(3);               // 加粗边框
          console.log('✅ 设置边框宽度成功');
      }
      if (typeof polygon.setOpacity === 'function') {
          polygon.setOpacity(1);              // 边框透明度
          console.log('✅ 设置边框透明度成功');
      }
      if (typeof polygon.setFillColor === 'function') {
          polygon.setFillColor('#00FF00');    // 绿色填充
          console.log('✅ 设置填充颜色成功');
      }
      if (typeof polygon.setFillOpacity === 'function') {
          polygon.setFillOpacity(0.4);        // 增加填充透明度
          console.log('✅ 设置填充透明度成功');
      }
      
      console.log('🎉 多边形高亮完成!当前高亮状态:', !!highlightedPolygon.value);
  } catch (error) {
      console.error('❌ 设置高亮样式失败:', error);
      console.log('多边形对象:', polygon);
      console.log('可用的方法:', Object.getOwnPropertyNames(polygon));
  }
}

// 取消高亮
function cancelHighlight(polygon: any) {
  if(polygon){
      console.log('🔄 清除传过来区域的高亮:', polygon._uniqueId || 'unknown');
      //清除传过来区域的高亮,恢复原始样式
      const originalStyle = originalStyles.value.get(polygon);
      if (originalStyle) {
          try {
              // 使用天地图的正确方法恢复样式
              if (typeof polygon.setColor === 'function') {
                  polygon.setColor(originalStyle.color);
                  console.log('✅ 恢复边框颜色成功');
              }
              if (typeof polygon.setWeight === 'function') {
                  polygon.setWeight(originalStyle.weight);
                  console.log('✅ 恢复边框宽度成功');
              }
              if (typeof polygon.setOpacity === 'function') {
                  polygon.setOpacity(originalStyle.opacity);
                  console.log('✅ 恢复边框透明度成功');
              }
              if (typeof polygon.setFillColor === 'function') {
                  polygon.setFillColor(originalStyle.fillColor);
                  console.log('✅ 恢复填充颜色成功');
              }
              if (typeof polygon.setFillOpacity === 'function') {
                  polygon.setFillOpacity(originalStyle.fillOpacity);
                  console.log('✅ 恢复填充透明度成功');
              }
          } catch (error) {   
              console.error('❌ 恢复原始样式失败:', error);
          }
      } else {
          console.log('⚠️ 未找到原始样式,使用默认样式');
          // 如果没有原始样式,使用默认样式
          try {
              if (typeof polygon.setColor === 'function') {
                  polygon.setColor('#FF0000');
              }
              if (typeof polygon.setWeight === 'function') {
                  polygon.setWeight(2);
              }
              if (typeof polygon.setOpacity === 'function') {
                  polygon.setOpacity(1);
              }
              if (typeof polygon.setFillColor === 'function') {
                  polygon.setFillColor('#FF0000');
              }
              if (typeof polygon.setFillOpacity === 'function') {
                  polygon.setFillOpacity(0.1);
              }
          } catch (error) {
              console.error('❌ 设置默认样式失败:', error);
          }
      }
      
      // 重要:清除高亮状态
      if (highlightedPolygon.value === polygon) {
          console.log('🗑️ 清除高亮状态引用');
          highlightedPolygon.value = null;
      }
      console.log('🔄 取消高亮完成,当前高亮状态:', !!highlightedPolygon.value);
  }
}



// 地图初始化完成后获取地图实例
onMounted(() => {
  console.log('组件已挂载,等待天地图初始化...');
  // 现在主要通过@init事件获取地图实例,这里只作为备用方案
  setTimeout(() => {
      if (!mapInstance.value) {
          console.log('通过@init事件未获取到地图实例,尝试备用方案...');

          // 备用方案:检查全局天地图对象
          if ((window as any).T && (window as any).T.Map) {
              try {
                  const mapContainer = document.querySelector('.mapDiv');
                  if (mapContainer) {
                      const newMap = new (window as any).T.Map(mapContainer, {
                          projection: 'EPSG:4326'
                      });
                      mapInstance.value = newMap;
                      console.log('备用方案:通过全局T对象创建地图实例成功');
                  }
              } catch (error) {
                  console.log('备用方案创建地图实例失败:', error);
              }
          }
      }
  }, 5000); // 5秒后检查备用方案
});
</script>

<script lang="ts">
export default { name: "demo-mousetool" };
</script>

<style scoped>
.mapDiv {
  width: 100%;
  height: 80vh;
}
</style>

demo5.vue

<template>
  <hr>
  </hr>
  <el-button @click="openTool('markTool')">标点</el-button>
  <el-button @click="openTool('polygonTool')">画面</el-button>
  <el-button @click="openTool('polylineTool')">画线</el-button>
  <el-button @click="openTool('rectangleTool')">画矩形</el-button>
  <el-button @click="openTool('circleTool')">画圆</el-button>
  <el-button @click="openTool('paintBrushTool')">画笔</el-button>
  <br />
  <el-button @click="clearTool('markTool')">清除标点</el-button>
  <el-button @click="clearTool('polygonTool')">清除面</el-button>
  <el-button @click="clearTool('polylineTool')">清除线</el-button>
  <el-button @click="clearTool('rectangleTool')">清除矩形</el-button>
  <el-button @click="clearTool('circleTool')">清除圆</el-button>
  <el-button @click="clearTool('paintBrushTool')">清除画笔</el-button>
  <el-button @click="clearTool()">清除全部</el-button>

  <!-- 调试信息显示区域 -->
 

  <div class="mapDiv" :class="state.copyright ? '' : 'hide-copyright'">
    <tdt-map :center="state.center" :zoom="state.zoom" :controls="state.controls" @init="onInit">

      <tdt-mousetool ref="mousetoolRef" :markTool="{ follow: true }"
        :polylineTool="{ showLabel: true, color: '#00FF00', weight: 3 }"
        :polygonTool="{ showLabel: true, color: '#FF0000', fillColor: '#FF0000', fillOpacity: 0.3 }"
        :circleTool="{ showLabel: true, color: '#0000FF', fillColor: '#0000FF', fillOpacity: 0.3 }"
        :rectangleTool="{ showLabel: true, color: '#FF6600', fillColor: '#FF6600', fillOpacity: 0.3 }"
        @polyline-addpoint="addPolygonPoint" @polyline-draw="onPolylineDraw" @polygon-draw="onPolygonDraw"
        @circle-draw="onCircleDraw" @rectangle-draw="onRectangleDraw" @mark-mouseup="onMarkMouseup">
      </tdt-mousetool>
    </tdt-map>
  </div>
</template>

<script lang="ts" setup>
import { reactive, ref, onMounted } from "vue";

const state = reactive({
  center: [113.280637, 23.125178],
  zoom: 11,
});
const mousetoolRef = ref();
const timer1 = ref<any>(null)
const timer2 = ref<any>(null)
const mapInstance = ref<any>(null)

// 为不同工具类型维护单独的标注数组
const circleLabels = ref<any[]>([]);
const rectangleLabels = ref<any[]>([]);
const polygonLabels = ref<any[]>([]);
const polylineLabels = ref<any[]>([]);
const markLabels = ref<any[]>([]);

// 调试信息
const debugInfo = reactive({
  show: false,
  coordCount: 0,
  coordinates: [] as Array<{lng: number, lat: number}>,
  center: {} as {lng: number, lat: number},
  area: 0,
  perimeter: 0
})


function openTool(toolName: string) {
  mousetoolRef.value?.open(toolName);
}

function clearTool(toolName?: string) {
  if (toolName) {
    // 清除指定的工具
    mousetoolRef.value?.clear(toolName);
    
    // 根据工具类型清除对应的文本标注
    switch (toolName) {
      case 'circleTool':
        clearTextLabels('circle');
        break;
      case 'rectangleTool':
        clearTextLabels('rectangle');
        break;
      case 'polygonTool':
        clearTextLabels('polygon');
        break;
      case 'polylineTool':
        clearTextLabels('polyline');
        break;
      case 'markTool':
        clearTextLabels('mark');
        break;
      case 'paintBrushTool':
        // 画笔工具可能没有对应的标注类型,暂时跳过
        break;
    }
  } else {
    // 清除所有工具和文本标注
    mousetoolRef.value?.clearAll();
    clearTextLabels();
  }
}

function onMarkMouseup(e: any) {
  console.log('标记点事件:', e);
}
function addPolygonPoint(e: any) {
  //currentLnglats格式 [{lat: 23.29921,lng: 113.01361},{lat: 23.26262,lng: 113.09189}]
  const currentLnglats = e.currentLnglats

}


// 画线完成事件
function onPolylineDraw(e: any) {
  console.log('画线完成事件:', e);

}

// 画面完成事件
function onPolygonDraw(e: any) {
  console.log('画面完成事件:', e);

}

// 画圆完成事件
function onCircleDraw({ type, target, currentCenter, currentRadius, currentCircle, allCircles }: any) {
  console.log('画圆完成事件:', { type, target, currentCenter, currentRadius, currentCircle, allCircles });

  if (currentCenter && currentRadius) {
    // 使用currentCenter和currentRadius计算面积和周长
    const center = {
      lng: currentCenter.lng || currentCenter.x,
      lat: currentCenter.lat || currentCenter.y
    };
    
    // 半径转换为米(如果currentRadius是像素值,需要转换)
    const radiusInMeters = currentRadius;
    const area = Math.PI * radiusInMeters * radiusInMeters;
    const perimeter = 2 * Math.PI * radiusInMeters;
    
    console.log('圆形计算结果:', {center, radiusInMeters, area, perimeter});
    
    // 在圆心添加标签显示面积和周长
    addLabelToMap(center, `面积: ${(area / 1000000).toFixed(2)} km²\n周长: ${(perimeter / 1000).toFixed(2)} km`, 'circle');

  } else if (currentCircle && currentCircle.getCenter && currentCircle.getRadius) {
    // 如果currentCircle有getCenter和getRadius方法,使用它们
    try {
      const center = currentCircle.getCenter();
      const radius = currentCircle.getRadius();

      if (center && radius) {
        const centerPoint = {
          lng: center.lng || center.x,
          lat: center.lat || center.y
        };

        const area = Math.PI * radius * radius;
        const perimeter = 2 * Math.PI * radius;


        addLabelToMap(centerPoint, `面积: ${(area / 1000000).toFixed(2)} km²\n周长: ${(perimeter / 1000).toFixed(2)} km`, 'circle');
      }
    } catch (error) {
      console.error('获取圆形数据失败:', error);
    }
  } else {
    // 使用地图中心点作为备用
    const center = { lat: state.center[1], lng: state.center[0] };
    addLabelToMap(center, '圆形绘制完成\n请检查控制台获取详细信息', 'circle');
  }
}

// 画矩形完成事件
function onRectangleDraw({ type, target, currentBounds, currentRectangle, allRectangles }: any) {
  console.log('画矩形完成事件:', { type, target, currentBounds, currentRectangle, allRectangles });
  const st = currentBounds.Lq  //左上角起点
  const sw = currentBounds.kq  //右下角终点
  
  // 计算矩形的面积和周长
  if (st && sw) {
    // 计算矩形的宽度和高度(米)
    const width = calculateDistance(st.lat, st.lng, st.lat, sw.lng); // 东西方向距离
    const height = calculateDistance(st.lat, st.lng, sw.lat, st.lng); // 南北方向距离
    
    // 计算面积和周长
    const area = width * height;
    const perimeter = 2 * (width + height);
    
    // 更新调试信息
    debugInfo.show = true;
    debugInfo.coordCount = 2;
    debugInfo.coordinates = [
      { lng: st.lng, lat: st.lat },
      { lng: sw.lng, lat: sw.lat }
    ];
    debugInfo.center = {
      lng: (st.lng + sw.lng) / 2,
      lat: (st.lat + sw.lat) / 2
    };
    debugInfo.area = area;
    debugInfo.perimeter = perimeter;
    
    console.log('矩形计算结果:', {
      start: st,
      end: sw,
      width: width,
      height: height,
      area: area,
      perimeter: perimeter
    });
    
    // 在矩形中心添加标签显示面积和周长
    const centerPoint = {
      lng: (st.lng + sw.lng) / 2,
      lat: (st.lat + sw.lat) / 2
    };
    
    addLabelToMap(centerPoint, `矩形面积: ${(area / 1000000).toFixed(4)} km²\n周长: ${(perimeter / 1000).toFixed(2)} km`, 'rectangle');
  }
}

// 计算圆的半径(米)
function calculateDistance(lat1: number, lng1: number, lat2: number, lng2: number): number {
  // 使用Haversine公式计算两点之间的距离
  const R = 6371000; // 地球半径(米)
  const dLat = (lat2 - lat1) * Math.PI / 180;
  const dLng = (lng2 - lng1) * Math.PI / 180;
  const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
    Math.sin(dLng / 2) * Math.sin(dLng / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  return R * c;
}

// 计算圆的半径(米)
function calculateCircleRadius(lnglats: any[]) {
  if (lnglats.length < 2) return 0;

  const center = lnglats[0];
  const point = lnglats[1];

  // 使用Haversine公式计算距离
  const R = 6371000; // 地球半径(米)
  const lat1 = center.lat * Math.PI / 180;
  const lat2 = point.lat * Math.PI / 180;
  const deltaLat = (point.lat - center.lat) * Math.PI / 180;
  const deltaLng = (point.lng - center.lng) * Math.PI / 180;

  const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
    Math.cos(lat1) * Math.cos(lat2) *
    Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

  return R * c;
}



// 在地图上添加标签
function addLabelToMap(position: any, text: string, labelType: string = 'circle') {
  console.log('准备添加标签:', position, text, '类型:', labelType);
  
  if (timer1.value) {
    clearTimeout(timer1.value);
  }
  
  timer1.value = setTimeout(() => {
    // 在 position 添加文本标注
    const label = new T.Label({
      text: text,
      position: position,
    });
    
    // 根据类型保存标注引用到对应的数组中
    switch (labelType) {
      case 'circle':
        circleLabels.value.push(label);
        break;
      case 'rectangle':
        rectangleLabels.value.push(label);
        break;
      case 'polygon':
        polygonLabels.value.push(label);
        break;
      case 'polyline':
        polylineLabels.value.push(label);
        break;
      case 'mark':
        markLabels.value.push(label);
        break;
      default:
        circleLabels.value.push(label);
    }
    
    // 将标注添加到地图
    mapInstance.value.addOverLay(label);
  }, 1000);
}
// 清除指定类型的文本标注
function clearTextLabels(labelType?: string) {
  let labelsToClear: any[] = [];
  let typeName = '';
  
  if (labelType) {
    // 清除指定类型的标注
    switch (labelType) {
      case 'circle':
        labelsToClear = circleLabels.value;
        typeName = '圆形';
        break;
      case 'rectangle':
        labelsToClear = rectangleLabels.value;
        typeName = '矩形';
        break;
      case 'polygon':
        labelsToClear = polygonLabels.value;
        typeName = '多边形';
        break;
      case 'polyline':
        labelsToClear = polylineLabels.value;
        typeName = '线条';
        break;
      case 'mark':
        labelsToClear = markLabels.value;
        typeName = '标记点';
        break;
      default:
        console.warn('未知的标注类型:', labelType);
        return;
    }
  } else {
    // 清除所有类型的标注
    labelsToClear = [
      ...circleLabels.value,
      ...rectangleLabels.value,
      ...polygonLabels.value,
      ...polylineLabels.value,
      ...markLabels.value
    ];
    typeName = '所有';
  }
  
  if (labelsToClear.length > 0 && mapInstance.value) {
    labelsToClear.forEach(label => {
      try {
        mapInstance.value.removeOverLay(label);
      } catch (error) {
        console.warn('清除文本标注时出错:', error);
      }
    });
    
    // 清空对应的数组
    if (labelType) {
      switch (labelType) {
        case 'circle':
          circleLabels.value = [];
          break;
        case 'rectangle':
          rectangleLabels.value = [];
          break;
        case 'polygon':
          polygonLabels.value = [];
          break;
        case 'polyline':
          polylineLabels.value = [];
          break;
        case 'mark':
          markLabels.value = [];
          break;
      }
    } else {
      // 清空所有数组
      circleLabels.value = [];
      rectangleLabels.value = [];
      polygonLabels.value = [];
      polylineLabels.value = [];
      markLabels.value = [];
    }
    
    console.log(`已清除${typeName}文本标注`);
  }
}

const onInit =(e)=>{
  mapInstance.value = e
}
</script>

<script lang="ts">
export default { name: "demo-control" };
</script>

<style scoped>
.mapDiv {
  width: 100%;
  height: 80%;
}

::v-deep.hide-copyright .tdt-control-copyright.tdt-control>div:not(.tdt-control-copyright) {
  display: none;
}

.mark-points-info {
  margin: 20px 0;
  padding: 15px;
  background-color: #f5f5f5;
  border-radius: 8px;
  border: 1px solid #ddd;
  display: flex;
}

.mark-points-info h3 {
  margin: 0 0 15px 0;
  color: #333;
  font-size: 16px;
}

.point-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 0;
  border-bottom: 1px solid #eee;
}

.point-item:last-child {
  border-bottom: none;
}

.point-item span {
  font-family: monospace;
  color: #666;
}

.debug-info {
  margin: 20px 0;
  padding: 15px;
  background-color: #f8f9fa;
  border-radius: 8px;
  border: 1px solid #dee2e6;
  font-family: monospace;
  font-size: 14px;
}

.debug-info h3 {
  margin: 0 0 15px 0;
  color: #495057;
  font-size: 16px;
}

.debug-item {
  margin: 10px 0;
  padding: 8px;
  background-color: #ffffff;
  border-radius: 4px;
  border: 1px solid #e9ecef;
}

.debug-item strong {
  color: #495057;
  display: inline-block;
  min-width: 120px;
}

.debug-item pre {
  margin: 8px 0 0 0;
  padding: 8px;
  background-color: #f8f9fa;
  border-radius: 4px;
  border: 1px solid #e9ecef;
  font-size: 12px;
  white-space: pre-wrap;
  word-break: break-all;
}
</style>

demo6.vue

<template>
    <hr>
    </hr>
    <el-button @click="highlightArea('新都区')">高亮新都区</el-button>
    <el-button @click="highlightArea('成华区')">高亮成华区</el-button>
    <el-button @click="highlightArea('all')">成都市边界框选</el-button>
    <el-button @click="isShowCloudMarker1 = !isShowCloudMarker1">添加密集点1</el-button>
    <el-button @click="isShowCloudMarker2 = !isShowCloudMarker2">添加密集点2</el-button>
    <el-button @click="clearHighlight">清除高亮</el-button>
    <el-button @click="fitBounds">适应边界</el-button>
    
    <tdt-map :center="state.center" :zoom="state.zoom" map-style="black" style="height: 80vh;"  @init="onInit">
        <tdt-cloud-marker-collection
        v-if="isShowCloudMarker1"
        :lnglats="state.lnglats1"
        color="red"
        SizeType="small"
        @click="onClick"
      ></tdt-cloud-marker-collection>
      <tdt-cloud-marker-collection
        v-if="isShowCloudMarker2"
        :lnglats="state.lnglats2"
        color="blue"
        SizeType="big"
        @click="onClick"
      ></tdt-cloud-marker-collection>
    </tdt-map>
    
    <!-- 信息显示区域 -->
    <div class="info-panel" v-if="selectedArea">
        <h3>选中区域信息</h3>
        <p><strong>名称:</strong> {{ selectedArea.name }}</p>
        <p><strong>行政代码:</strong> {{ selectedArea.adcode }}</p>
        <p><strong>级别:</strong> {{ selectedArea.level }}</p>
        <p><strong>中心坐标:</strong> {{ selectedArea.center?.join(', ') }}</p>
    </div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import mapJson from './chengdu.json'
import chengduLine from './chengduLine.json'

const mapInstance = ref<any>(null)
//是否显示海量密集点
const isShowCloudMarker1 = ref(false)
const isShowCloudMarker2 = ref(false)
const selectedArea = ref<any>(null)
const currentHighlights = ref<any[]>([]) // 改为数组,支持多个高亮区域

const state = reactive({
    center: [104.0657, 30.6594],
    lnglats1: mapJson.features.map((item: any) => item.properties.center),
    lnglats2: mapJson.features.map((item: any) => item.properties.centroid),
    zoom: 9
})

const onInit = (map: any) => {
    mapInstance.value = map;
    drawChengduBoundary();
}

const highlightArea = (data: any) => {
    if (data === 'all') {
        // 成都市边界线高亮 - 只显示边界线
        highlightChengduBoundary();
    } else {
        // 点击的区域高亮 - 支持叠加高亮
        const targetArea = mapJson.features.find((item: any) => item.properties.name === data);
        if (targetArea) {
            // 检查是否已经高亮过
            const isAlreadyHighlighted = currentHighlights.value.some(highlight => 
                highlight.properties && highlight.properties.name === data
            );
            
            if (!isAlreadyHighlighted) {
                highlightSpecificArea(targetArea);
                selectedArea.value = targetArea.properties;
            }
        }
    }
}

const highlightChengduBoundary = () => {
    if (!mapInstance.value) return;
    
    // 获取最外层边界线
    const boundaryLines = chengduLine.features[0].geometry.coordinates[0];
    
    // 为每条边界线创建单独的线段
    boundaryLines.forEach((line: number[][]) => {
        // 将边界线转换为天地图格式
        const tiandituLine = line.map(coord => new T.LngLat(coord[0], coord[1]));
        
        // 创建边界线(使用Polyline而不是Polygon)
        const boundaryLine = new T.Polyline(tiandituLine, {
            color: '#00ff00', // 绿色边界线
            weight: 3,        // 粗线条
            opacity: 0.8,     // 高透明度
            lineStyle: 'solid'
        });
        
        // 添加到地图
        mapInstance.value.addOverLay(boundaryLine);
        currentHighlights.value.push(boundaryLine);
    });
    
    selectedArea.value = { name: '成都市最外层边界', level: 'city' };
}

const highlightSpecificArea = (area: any) => {
    if (!mapInstance.value) return;
    
    // 使用天地图API绘制区域高亮
    if (area.geometry && area.geometry.coordinates) {
        const polygons = convertGeoJSONToTianditu(area.geometry.coordinates, area.geometry.type);
        
        polygons.forEach(polygon => {
            const highlightPolygon = new T.Polygon(polygon, {
                weight: 2,
                opacity: 0.8, //透明度
                color: '#' + Math.floor(Math.random()*16777215).toString(16),
                fillColor: '#' + Math.floor(Math.random()*16777215).toString(16),
                fillOpacity: 1, //填充透明度 1 完全透明 0 
                lineStyle: 'solid'
            });
            
            // 添加到地图
            mapInstance.value.addOverLay(highlightPolygon);
            currentHighlights.value.push(highlightPolygon);
        });
    }
}

const clearHighlight = () => {
    if (!mapInstance.value || currentHighlights.value.length === 0) return;
    
    try {
        // 移除所有高亮图层
        currentHighlights.value.forEach(highlight => {
            if (highlight) {
                mapInstance.value.removeOverLay(highlight);
            }
        });
        
        currentHighlights.value = [];
        selectedArea.value = null;
    } catch (error) {
        console.log('清除高亮时出错:', error);
    }
}

const fitBounds = () => {
    if (!mapInstance.value) return;
    
    // 计算所有区域的边界
    const bounds = calculateBounds();
    if (bounds) {
        const center = new T.LngLat(
            (bounds[0][0] + bounds[1][0]) / 2,
            (bounds[0][1] + bounds[1][1]) / 2
        );
        
        // 计算合适的缩放级别
        const lngDiff = Math.abs(bounds[1][0] - bounds[0][0]);
        const latDiff = Math.abs(bounds[1][1] - bounds[0][1]);
        const maxDiff = Math.max(lngDiff, latDiff);
        
        let zoom = 11;
        if (maxDiff > 0.5) zoom = 8;
        else if (maxDiff > 0.2) zoom = 9;
        else if (maxDiff > 0.1) zoom = 10;
        else if (maxDiff > 0.05) zoom = 11;
        else if (maxDiff > 0.02) zoom = 12;
        else zoom = 13;
        
        mapInstance.value.centerAndZoom(center, zoom);
    }
}

const calculateBounds = () => {
    if (!mapJson.features || mapJson.features.length === 0) return null;
    
    let minLng = Infinity, maxLng = -Infinity;
    let minLat = Infinity, maxLat = -Infinity;
    
    mapJson.features.forEach((feature: any) => {
        if (feature.geometry && feature.geometry.coordinates) {
            feature.geometry.coordinates.forEach((polygon: any) => {
                polygon.forEach((ring: any) => {
                    ring.forEach((coord: any) => {
                        minLng = Math.min(minLng, coord[0]);
                        maxLng = Math.max(maxLng, coord[0]);
                        minLat = Math.min(minLat, coord[1]);
                        maxLat = Math.max(maxLat, coord[1]);
                    });
                });
            });
        }
    });
    
    if (minLng !== Infinity && maxLng !== -Infinity) {
        return [[minLng, minLat], [maxLng, maxLat]];
    }
    
    return null;
}


// 找到最外层的边界线
const findOutermostBoundaries = (boundaries: [number, number][][]): [number, number][][] => {
    if (boundaries.length === 0) return [];
    
    // 简化处理:返回所有边界线,但标记为最外层
    // 在实际应用中,这里需要更复杂的算法来判断哪些边界线是最外层的
    return boundaries;
}

// 转换GeoJSON坐标为天地图格式
const convertGeoJSONToTianditu = (coordinates: any, type: string) => {
    const polygons: T.LngLat[][] = [];
    
    if (type === 'Polygon') {
        const points = coordinates[0].map((coord: any) => new T.LngLat(coord[0], coord[1]));
        polygons.push(points);
    } else if (type === 'MultiPolygon') {
        coordinates.forEach((polygon: any) => {
            const points = polygon[0].map((coord: any) => new T.LngLat(coord[0], coord[1]));
            polygons.push(points);
        });
    }
    
    return polygons;
}

const drawChengduBoundary = () => {
    if (!mapInstance.value) return;
    
    console.log('地图实例:', mapInstance.value);
    
    // 使用天地图API绘制成都市区域边界
    mapJson.features.forEach((feature: any) => {
        if (feature.geometry && feature.geometry.coordinates) {
            const polygons = convertGeoJSONToTianditu(feature.geometry.coordinates, feature.geometry.type);
            
            polygons.forEach(polygon => {
                // 创建多边形
                const tPolygon = new T.Polygon(polygon, {
                    color: '#ffffff',
                    weight: 1,
                    opacity: 0.6,
                    fillColor: '#4a90e2',
                    fillOpacity: 0.1,
                    lineStyle: 'solid'
                });
                
                // 添加到地图
                mapInstance.value.addOverLay(tPolygon);
                
                // 绑定点击事件
                // tPolygon.addEventListener('click', (e: any) => {
                //     console.log('点击区域:', feature.properties);
                //     selectedArea.value = feature.properties;
                //     highlightSpecificArea(feature);
                // });
                
                // 鼠标悬停效果
                tPolygon.addEventListener('mouseover', () => {
                    // 检查当前多边形是否已经被高亮
                    const isHighlighted = currentHighlights.value.some(highlight => 
                        highlight === tPolygon || 
                        (highlight.properties && highlight.properties.name === feature.properties.name)
                    );
                    
                    if (!isHighlighted) {
                        tPolygon.setFillColor('#00BFFF');
                        tPolygon.setFillOpacity(0.4);
                        tPolygon.setColor('#FFFF00');
                        tPolygon.setWeight(2);
                    }
                });
                
                tPolygon.addEventListener('mouseout', () => {
                    // 检查当前多边形是否已经被高亮
                    const isHighlighted = currentHighlights.value.some(highlight => 
                        highlight === tPolygon || 
                        (highlight.properties && highlight.properties.name === feature.properties.name)
                    );
                    
                    if (!isHighlighted) {
                        tPolygon.setFillColor('#4a90e2');
                        tPolygon.setFillOpacity(0.1);
                        tPolygon.setColor('#ffffff');
                        tPolygon.setWeight(1);
                    }
                });
            });
        }
    });
}

const onClick = (event: any) => {
    console.log('点击了密集点:', event);
    // 可以在这里添加点击密集点后的处理逻辑
}

// 组件卸载时清理
onMounted(() => {
    // 组件挂载后的初始化逻辑
})

</script>
<style scoped lang="scss">
.info-panel {
    margin-top: 20px;
    padding: 15px;
    background: #f5f5f5;
    border-radius: 8px;
    border: 1px solid #ddd;
    
    h3 {
        margin: 0 0 10px 0;
        color: #333;
    }
    
    p {
        margin: 5px 0;
        color: #666;
    }
}

.el-button {
    margin: 5px;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值