引言
安装依赖
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>