/**
* ECharts 地图足迹模块 (支持钻取功能 - 点击省份查看下一级, 点击按钮返回)
*/
const MapFootprintModule = (() => {
let myChart = null; // ECharts 实例
let currentMapLevel = 'country'; // 当前地图层级 ('country', 'province')
let currentProvinceCode = null; // 当前显示的省份编码 (如果有的话)
// --- 配置 ---
const COUNTRY_MAP_NAME = 'china_custom';
const COUNTRY_GEOJSON_PATH = './static/js/echarts/china.geojson';
const PROVINCE_GEOJSON_BASE_PATH = './static/js/echarts/province/'; // 假设省份 GeoJSON 存放在这个目录
// --- 数据 ---
// 注意:这里的坐标可能需要根据你实际访问的城市进行微调
const visitedPlaces = [
{ name: "济南市", value: [117.024961, 36.682788], province: "山东省" },
{ name: "青岛市", value: [120.384423, 36.065918], province: "山东省" },
{ name: "淄博市", value: [118.055915, 36.813547], province: "山东省" },
{ name: "潍坊市", value: [119.162775, 36.705759], province: "山东省" },
{ name: "北京市", value: [116.407395, 39.904211], province: "北京" },
{ name: "杭州市", value: [120.153576, 30.287459], province: "浙江" },
{ name: "苏州市", value: [120.585316, 31.298886], province: "江苏" },
{ name: "扬州市", value: [119.421003, 32.393159], province: "江苏" },
{ name: "保定市", value: [115.482331, 38.867657], province: "河北" },
{ name: "石家庄市", value: [114.514891, 38.042309], province: "河北" },
{ name: "衡水市", value: [115.665996, 37.739574], province: "河北" },
{ name: "洛阳市", value: [112.454174, 34.618139], province: "河南" },
{ name: "南京市", value: [118.796877, 32.060255], province: "江苏" },
{ name: "无锡市", value: [120.311987, 31.490920], province: "江苏" },
{ name: "沧州市", value: [116.838581, 38.308094], province: "河北" },
{ name: "镇江市", value: [119.452753, 32.204402], province: "江苏" },
];
// 省份名称到其 GeoJSON 文件名的映射 (需要根据你的文件名调整)
const provinceNameToGeoFile = {
"山东省": "shandong.json",
// 你可以继续添加其他省份的映射,例如:
// "江苏省": "jiangsu.json",
// "河北省": "hebei.json",
// ...
};
// --- 工具函数 ---
/**
* 根据省份中文名获取其对应的 GeoJSON 文件完整路径
* @param {string} provinceName - 省份中文名
* @returns {string|null} - 文件路径或 null
*/
const getProvinceGeoPath = (provinceName) => {
const fileName = provinceNameToGeoFile[provinceName];
if (fileName) {
return ` ${PROVINCE_GEOJSON_BASE_PATH}${fileName}`;
}
console.warn(`未找到省份 " ${provinceName}" 对应的 GeoJSON 文件。`);
return null;
};
/**
* 根据地图层级和可选的省份编码生成 ECharts 配置
* @param {string} level - 地图层级 ('country' or 'province')
* @param {string} [provinceCode] - 省份编码 (当 level='province' 时使用)
* @returns {Object} - ECharts 配置对象
*/
const getEChartsOption = (level, provinceCode = null) => {
let geoMapName, roamSetting, zoomLevel, centerCoord, titleText;
let filteredVisitedPlaces = []; // 用于过滤足迹点
if (level === 'country') {
geoMapName = COUNTRY_MAP_NAME;
roamSetting = false; // 启用缩放和平移
zoomLevel = 1.1;
centerCoord = [104.06, 30]; // 中国中心
titleText = '';
filteredVisitedPlaces = visitedPlaces; // 显示所有足迹
} else if (level === 'province' && provinceCode) {
geoMapName = `province_ ${provinceCode}`; // 为省份地图创建唯一名称
roamSetting = false; // 省份地图也启用缩放平移
zoomLevel = 1.0; // 可以根据需要调整
centerCoord = [118.5, 36.5]; // 示例:山东中心
titleText = ` ${provinceCode} - 我的足迹`;
// 过滤出属于当前省份的足迹点
filteredVisitedPlaces = visitedPlaces.filter(place => place.province === provinceCode);
} else {
console.error("无效的地图层级或省份代码");
return {}; // 返回空配置
}
return {
title: {
text: titleText,
// subtext: level === 'country' ? '点击省份进入详情' : '点击下方返回按钮返回全国地图',
subtext: '读万卷书,不如行万里路。见多才会识广~',
left: 'center',
textStyle: {
fontSize: 18
},
subtextStyle: {
fontSize: 12
}
},
tooltip: {
trigger: 'item',
formatter: function(params) {
if (params.componentType === 'series' && params.seriesType === 'scatter') {
// 足迹点的 tooltip
if (params.value && params.value.length >= 2) {
return params.name + '
经度: ' + params.value[0].toFixed(4) + '
纬度: ' + params.value[1].toFixed(4);
}
return params.name;
} else if (params.componentType === 'geo') {
// 地图区域的 tooltip
return params.name;
}
return params.name;
}
},
geo: {
map: geoMapName,
roam: roamSetting,
zoom: zoomLevel,
center: centerCoord,
emphasis: {
itemStyle: {
areaColor: '#f0f0f0',
borderColor: '#409EFF',
borderWidth: 1
}
},
itemStyle: {
areaColor: '#eef2ff',
borderColor: '#333'
}
},
series: [
{
name: '足迹',
type: 'scatter',
coordinateSystem: 'geo',
data: filteredVisitedPlaces,
symbol: 'pin',
symbolSize: 18,
itemStyle: {
color: '#FF6B6B',
borderColor: '#fff',
borderWidth: 1,
},
emphasis: {
itemStyle: {
color: '#dd4444'
}
},
tooltip: {
formatter: function(params) {
return ` ${params.data.name}
坐标: [ ${params.data.value[0].toFixed(4)}, ${params.data.value[1].toFixed(4)}]`;
}
}
}
]
};
};
/**
* 加载并注册指定路径的 GeoJSON 地图数据
* @param {string} path - GeoJSON 文件路径
* @param {string} mapName - 注册到 ECharts 的地图名称
* @returns {Promise} - 成功或失败
*/
const loadAndRegisterSingleMap = async (path, mapName) => {
try {
console.log(`正在加载 GeoJSON: ${path}`);
const response = await fetch(path);
if (!response.ok) {
throw new Error(`加载 GeoJSON 失败: ${response.status} ${response.statusText}`);
}
const geoJsonData = await response.json();
// 注册地图
echarts.registerMap(mapName, geoJsonData);
console.log(`GeoJSON 地图数据 ${mapName} 加载并注册成功。`);
return true;
} catch (error) {
console.error('加载或注册 GeoJSON 地图时出错:', error);
return false;
}
};
/**
* 切换到指定的地图层级和区域
* @param {string} targetLevel - 目标层级 ('country' or 'province')
* @param {string} [targetProvinceCode] - 目标省份编码 (当 targetLevel='province' 时使用)
*/
const switchMap = async (targetLevel, targetProvinceCode = null) => {
if (!myChart) {
console.error("ECharts 实例不存在,无法切换地图。");
return;
}
let mapNameToUse, geoPathToLoad;
if (targetLevel === 'country') {
mapNameToUse = COUNTRY_MAP_NAME;
geoPathToLoad = COUNTRY_GEOJSON_PATH;
currentMapLevel = 'country';
currentProvinceCode = null;
} else if (targetLevel === 'province' && targetProvinceCode) {
mapNameToUse = `province_ ${targetProvinceCode}`;
const path = getProvinceGeoPath(targetProvinceCode);
if (!path) {
console.error(`无法切换到省份 ${targetProvinceCode}: 找不到对应的 GeoJSON 文件。`);
return;
}
geoPathToLoad = path;
currentMapLevel = 'province';
currentProvinceCode = targetProvinceCode;
} else {
console.error("无效的目标层级或省份代码。");
return;
}
// 尝试加载并注册目标地图数据
const loadSuccess = await loadAndRegisterSingleMap(geoPathToLoad, mapNameToUse);
if (!loadSuccess) {
console.error(`切换地图失败:无法加载 ${geoPathToLoad}`);
return;
}
// 生成新配置并应用
const newOption = getEChartsOption(targetLevel, targetProvinceCode);
myChart.setOption(newOption, true); // 使用 notMerge=true 确保完全替换
// 更新返回按钮的可见性
updateBackButtonVisibility();
};
// --- 新增函数:管理返回按钮 ---
/**
* 获取返回按钮的DOM元素
* @returns {HTMLElement|null}
*/
const getBackButtonElement = () => {
// 假设按钮ID是固定的,可以根据需要调整
return document.getElementById('map-back-button');
};
/**
* 创建返回按钮并将其添加到图表容器中
* @param {HTMLElement} chartContainer - ECharts 图表容器
*/
const createBackButton = (chartContainer) => {
const button = document.createElement('button');
button.id = 'map-back-button';
button.textContent = '⬅返回全国地图';
button.style.position = 'absolute';
button.style.top = '10px';
button.style.left = '10px';
button.style.zIndex = 1000; // 确保按钮在图表之上
button.style.padding = '2px 5px'; // 减少内边距以更接近纯文本
button.style.fontSize = '14px'; // 根据需要调整字体大小
button.style.cursor = 'pointer';
button.style.border = 'none'; // 去掉边框
button.style.backgroundColor = 'transparent'; // 设置背景透明
button.style.display = 'none'; // 初始隐藏
button.onclick = () => {
console.log("点击了返回按钮。");
switchMap('country');
};
chartContainer.appendChild(button);
};
/**
* 根据当前地图层级更新返回按钮的可见性
*/
const updateBackButtonVisibility = () => {
const button = getBackButtonElement();
if (!button) {
console.warn("返回按钮 DOM 元素未找到,无法更新其可见性。");
return;
}
// 当前在省份地图时显示按钮,否则隐藏
button.style.display = (currentMapLevel === 'province') ? 'block' : 'none';
};
// --- /新增函数 ---
/**
* 初始化 ECharts 地图 (初始加载国家地图)
* @param {HTMLElement} container - 图表容器DOM元素
*/
const initEChartsMap = async (container) => {
if (!container) {
console.error('地图容器未找到');
return;
}
// 初始化 ECharts 实例
myChart = echarts.init(container);
// 创建并添加返回按钮
createBackButton(container);
// 首先加载并注册国家地图
const countryLoadSuccess = await loadAndRegisterSingleMap(COUNTRY_GEOJSON_PATH, COUNTRY_MAP_NAME);
if (!countryLoadSuccess) {
console.error("初始化失败:无法加载国家 GeoJSON。");
return; // 或者显示错误信息给用户
}
// 应用初始配置 (国家地图)
const initialOption = getEChartsOption('country');
myChart.setOption(initialOption);
// --- 添加事件监听 ---
// 监听地图区域点击事件
myChart.on('click', 'geo', function (params) {
if (currentMapLevel === 'country') {
// 当前是国家地图,点击省份则进入
const clickedProvinceName = params.name;
console.log(`点击了省份: ${clickedProvinceName}`);
// 尝试获取省份对应的 GeoJSON 文件名
const fileName = Object.keys(provinceNameToGeoFile).find(key => key === clickedProvinceName);
console.log(fileName)
if (fileName) {
const provinceCode = fileName; // 这里假设文件名就是省份简称
console.log(`即将切换到省份: ${provinceCode}`);
switchMap('province', provinceCode);
} else {
console.log(`未配置省份 " ${clickedProvinceName}" 的详细地图。`);
}
}
// 如果在省份地图上点击,可以有其他逻辑,比如不处理
});
// 监听窗口大小变化,自动适配
window.addEventListener('resize', function () {
if (myChart) {
myChart.resize();
}
});
};
/**
* 获取地图容器ID
* @returns {string}
*/
const getMapContainerId = () => {
return 'echarts-map-container';
};
/**
* 渲染 ECharts 地图内容到指定容器
* @param {HTMLElement} contentDiv - 标签页的内容容器
*/
const renderMap = (contentDiv) => {
// 清空内容区域
// contentDiv.innerHTML = '';
// 创建地图容器
const mapDiv = document.createElement('div');
mapDiv.id = getMapContainerId();
mapDiv.style.width = '100%';
mapDiv.style.height = '600px';
mapDiv.style.marginTop = '20px';
mapDiv.style.position = 'relative'; // 为绝对定位的按钮提供参考
contentDiv.appendChild(mapDiv);
// 加载 GeoJSON 并初始化地图
setTimeout(async () => {
const container = document.getElementById(getMapContainerId());
if (container) {
await initEChartsMap(container); // 使用 await 确保初始化完成
} else {
console.error('地图容器 DOM 元素未找到,无法初始化图表。');
}
}, 100);
};
/**
* 销毁 ECharts 实例 (可选,用于性能优化或切换时清理)
*/
const destroyMap = () => {
if (myChart) {
myChart.dispose();
myChart = null;
console.log('ECharts 地图已销毁');
// 可以考虑移除返回按钮
const button = getBackButtonElement();
if (button && button.parentNode) {
button.parentNode.removeChild(button);
}
}
};
// 返回公共方法
return {
renderMap,
destroyMap
};
})();