|
@@ -73,30 +73,6 @@
|
|
|
ref="townshipMapRef"
|
|
ref="townshipMapRef"
|
|
|
class="township-map-container"
|
|
class="township-map-container"
|
|
|
></div>
|
|
></div>
|
|
|
- </div>
|
|
|
|
|
- <!-- 新增:要素详细信息栏 -->
|
|
|
|
|
- <div v-if="currentFeatureInfo" class="feature-info-panel">
|
|
|
|
|
- <h4 style="margin-top: 0; color: #0066CC; border-bottom: 1px solid #eee; padding-bottom: 10px;">
|
|
|
|
|
- 地块详细信息
|
|
|
|
|
- </h4>
|
|
|
|
|
- <ul style="list-style: none; padding: 0; margin: 0;">
|
|
|
|
|
- <li style="margin-bottom: 8px; font-size: 14px;">
|
|
|
|
|
- <strong>水稻镉含量:</strong>{{ currentFeatureInfo.avgmean.toFixed(4) }} mg/kg
|
|
|
|
|
- </li>
|
|
|
|
|
- <li style="margin-bottom: 8px; font-size: 14px;">
|
|
|
|
|
- <strong>镉输入通量:</strong>{{ currentFeatureInfo.lncd.toFixed(4) }} g/ha/a
|
|
|
|
|
- <!-- 若有单位可补充:{{ currentFeatureInfo.lncdUnit }} -->
|
|
|
|
|
- </li>
|
|
|
|
|
- <li style="margin-bottom: 8px; font-size: 14px;">
|
|
|
|
|
- <strong>镉输出通量:</strong>{{ currentFeatureInfo.outcd.toFixed(4) }} g/ha/a
|
|
|
|
|
- </li>
|
|
|
|
|
- <li style="margin-bottom: 8px; font-size: 14px;">
|
|
|
|
|
- <strong>镉净通量:</strong>{{ currentFeatureInfo.netcd.toFixed(4) }} g/ha/a
|
|
|
|
|
- </li>
|
|
|
|
|
- <li style="margin-bottom: 8px; font-size: 14px;">
|
|
|
|
|
- <strong>当年镉浓度:</strong>{{ currentFeatureInfo.endcd.toFixed(4) }} g/ha/a
|
|
|
|
|
- </li>
|
|
|
|
|
- </ul>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
<div v-if="!loadingTownshipMap && !townshipMapInstance" class="no-data">
|
|
<div v-if="!loadingTownshipMap && !townshipMapInstance" class="no-data">
|
|
|
<el-icon><Location /></el-icon>
|
|
<el-icon><Location /></el-icon>
|
|
@@ -209,6 +185,7 @@ export default {
|
|
|
},
|
|
},
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
|
|
+ currentScale: 0, // 当前地图比例尺
|
|
|
currentFeatureInfo: null, // 存储当前点击要素的详细信息
|
|
currentFeatureInfo: null, // 存储当前点击要素的详细信息
|
|
|
mapContainerStyle: {
|
|
mapContainerStyle: {
|
|
|
width: '100%',
|
|
width: '100%',
|
|
@@ -246,6 +223,16 @@ export default {
|
|
|
dataMap:{},
|
|
dataMap:{},
|
|
|
fluxDataMap: {}, // 存储通量Cd数据
|
|
fluxDataMap: {}, // 存储通量Cd数据
|
|
|
fluxDataLoaded: false, // 通量Cd数据是否已加载
|
|
fluxDataLoaded: false, // 通量Cd数据是否已加载
|
|
|
|
|
+ geoserverConfig: {
|
|
|
|
|
+ baseUrl: 'http://localhost:8080/geoserver',
|
|
|
|
|
+ workspace: 'soilgd',
|
|
|
|
|
+ wmsService: 'WMS',
|
|
|
|
|
+ version: '1.1.0'
|
|
|
|
|
+ },
|
|
|
|
|
+ currentLayerInfo: null, // 当前图层信息
|
|
|
|
|
+ layerLoading: false, // 图层加载状态
|
|
|
|
|
+ layerInfoFetched: false, // 新增:标记是否已获取图层信息
|
|
|
|
|
+ fetchingLayer: false, // 新增:标记是否正在获取中
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
|
|
|
|
@@ -285,6 +272,67 @@ export default {
|
|
|
|
|
|
|
|
|
|
|
|
|
methods: {
|
|
methods: {
|
|
|
|
|
+ // 新增:获取最新GeoServer图层信息
|
|
|
|
|
+ async fetchLatestLayerInfo() {
|
|
|
|
|
+ // 如果正在获取或已获取,直接返回
|
|
|
|
|
+ if (this.fetchingLayer || this.layerInfoFetched) {
|
|
|
|
|
+ return this.currentLayerInfo || this.getDefaultLayerInfo();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.fetchingLayer = true;
|
|
|
|
|
+ const response = await api8000.get('/api/geoserver/latest-layer');
|
|
|
|
|
+
|
|
|
|
|
+ if (response.data && response.data.geoserver && response.data.geoserver.exists) {
|
|
|
|
|
+ this.currentLayerInfo = response.data;
|
|
|
|
|
+ this.layerInfoFetched = true; // 标记为已获取
|
|
|
|
|
+ console.log('获取到最新图层信息:', this.currentLayerInfo);
|
|
|
|
|
+ return this.currentLayerInfo;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.warn('未找到最新图层信息,使用默认配置');
|
|
|
|
|
+ return this.getDefaultLayerInfo();
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取最新图层信息失败:', error);
|
|
|
|
|
+ return this.getDefaultLayerInfo();
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.fetchingLayer = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 新增:获取默认图层配置
|
|
|
|
|
+ getDefaultLayerInfo() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ geoserver: {
|
|
|
|
|
+ exists: true,
|
|
|
|
|
+ layer_name: 'shapeMap',
|
|
|
|
|
+ full_name: `${this.geoserverConfig.workspace}:shapeMap`,
|
|
|
|
|
+ workspace: this.geoserverConfig.workspace
|
|
|
|
|
+ },
|
|
|
|
|
+ wms: {
|
|
|
|
|
+ wms_url: `${this.geoserverConfig.baseUrl}/${this.geoserverConfig.workspace}/wms`,
|
|
|
|
|
+ layer_name: `${this.geoserverConfig.workspace}:shapeMap`
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 新增:构建WMS服务URL
|
|
|
|
|
+ getWmsUrl() {
|
|
|
|
|
+ if (this.currentLayerInfo && this.currentLayerInfo.wms) {
|
|
|
|
|
+ return this.currentLayerInfo.wms.wms_url;
|
|
|
|
|
+ }
|
|
|
|
|
+ return `${this.geoserverConfig.baseUrl}/${this.geoserverConfig.workspace}/wms`;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 新增:获取图层名称
|
|
|
|
|
+ getLayerName() {
|
|
|
|
|
+ if (this.currentLayerInfo && this.currentLayerInfo.geoserver) {
|
|
|
|
|
+ return this.currentLayerInfo.geoserver.full_name ||
|
|
|
|
|
+ `${this.geoserverConfig.workspace}:${this.currentLayerInfo.geoserver.layer_name}`;
|
|
|
|
|
+ }
|
|
|
|
|
+ return `${this.geoserverConfig.workspace}:shapeMap`;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
async fetchFluxData() {
|
|
async fetchFluxData() {
|
|
|
try {
|
|
try {
|
|
|
this.loadingTownshipMap = true;
|
|
this.loadingTownshipMap = true;
|
|
@@ -491,8 +539,19 @@ export default {
|
|
|
|
|
|
|
|
// 初始化乡镇边界地图
|
|
// 初始化乡镇边界地图
|
|
|
async initTownshipMap() {
|
|
async initTownshipMap() {
|
|
|
|
|
+ // 如果正在加载,直接返回
|
|
|
|
|
+ if (this.loadingTownshipMap) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
this.loadingTownshipMap = true;
|
|
this.loadingTownshipMap = true;
|
|
|
|
|
+
|
|
|
|
|
+ // 只有在未获取过图层信息时才获取
|
|
|
|
|
+ if (!this.layerInfoFetched) {
|
|
|
|
|
+ await this.fetchLatestLayerInfo();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (!this.fluxDataLoaded) {
|
|
if (!this.fluxDataLoaded) {
|
|
|
await this.fetchFluxData();
|
|
await this.fetchFluxData();
|
|
|
}
|
|
}
|
|
@@ -641,8 +700,7 @@ async initTownshipMap() {
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- // 使用Leaflet渲染乡镇地图并填充颜色
|
|
|
|
|
- // 修改渲染地图方法,增加错误处理
|
|
|
|
|
|
|
+
|
|
|
// 使用Leaflet渲染乡镇地图并填充颜色
|
|
// 使用Leaflet渲染乡镇地图并填充颜色
|
|
|
renderTownshipMap() {
|
|
renderTownshipMap() {
|
|
|
try {
|
|
try {
|
|
@@ -679,6 +737,15 @@ renderTownshipMap() {
|
|
|
attributionControl: false
|
|
attributionControl: false
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ this.townshipMapInstance.on('zoomend', () => {
|
|
|
|
|
+ this.handleZoomChange();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 添加移动事件监听,确保实时更新
|
|
|
|
|
+ this.townshipMapInstance.on('moveend', () => {
|
|
|
|
|
+ this.handleZoomChange();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
// 添加缩放控件
|
|
// 添加缩放控件
|
|
|
L.control.zoom({
|
|
L.control.zoom({
|
|
|
position: 'bottomright'
|
|
position: 'bottomright'
|
|
@@ -705,50 +772,51 @@ renderTownshipMap() {
|
|
|
fillOpacity: 0
|
|
fillOpacity: 0
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
- onEachFeature: (feature, layer) => {
|
|
|
|
|
- const townName = feature.properties.name;
|
|
|
|
|
|
|
+ onEachFeature: (feature, layer) => {
|
|
|
|
|
+ const townName = feature.properties.name;
|
|
|
|
|
+
|
|
|
|
|
+ const originalStyle = {
|
|
|
|
|
+ fillColor: 'transparent',
|
|
|
|
|
+ weight: 2,
|
|
|
|
|
+ opacity: 1,
|
|
|
|
|
+ color: '#000',
|
|
|
|
|
+ dashArray: '',
|
|
|
|
|
+ fillOpacity: 0
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 鼠标悬停事件 - 只显示镇级tooltip
|
|
|
|
|
+ layer.on('mouseover', (e) => {
|
|
|
|
|
+ // 实时检查比例尺
|
|
|
|
|
+ const currentScale = this.getCurrentMapScale();
|
|
|
|
|
|
|
|
- // 保存原始样式,用于恢复
|
|
|
|
|
- const originalStyle = {
|
|
|
|
|
- fillColor: 'transparent', // 固定为白色填充
|
|
|
|
|
- weight: 2,
|
|
|
|
|
- opacity: 1,
|
|
|
|
|
- color: '#000', // 黑色边界线
|
|
|
|
|
|
|
+ // 高亮当前区域(无论比例尺如何都高亮)
|
|
|
|
|
+ layer.setStyle({
|
|
|
|
|
+ weight: 6,
|
|
|
|
|
+ color: '#0066CC',
|
|
|
dashArray: '',
|
|
dashArray: '',
|
|
|
- fillOpacity: 0
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- // 鼠标悬停事件 - 修复版本
|
|
|
|
|
- layer.on('mouseover', (e) => {
|
|
|
|
|
- // 高亮当前区域
|
|
|
|
|
- layer.setStyle({
|
|
|
|
|
- weight: 6,
|
|
|
|
|
- color: '#0066CC',
|
|
|
|
|
- dashArray: '',
|
|
|
|
|
- fillOpacity: 0.95
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- // 显示tooltip
|
|
|
|
|
- this.showTooltip(layer, townName, e.latlng);
|
|
|
|
|
|
|
+ fillOpacity: 0.95
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // 鼠标离开事件 - 修复版本
|
|
|
|
|
- layer.on('mouseout', (e) => {
|
|
|
|
|
- // 恢复原始样式 - 使用保存的样式而不是resetStyle
|
|
|
|
|
- layer.setStyle(originalStyle);
|
|
|
|
|
-
|
|
|
|
|
- // 正确关闭tooltip
|
|
|
|
|
- if (layer._tooltip) {
|
|
|
|
|
- layer.closeTooltip();
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 只有在比例尺 >= 17.28 时才显示镇级tooltip
|
|
|
|
|
+ if (currentScale >= 17.28) {
|
|
|
|
|
+ this.showTownshipTooltip(layer, townName, e.latlng);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 鼠标离开事件
|
|
|
|
|
+ layer.on('mouseout', (e) => {
|
|
|
|
|
+ layer.setStyle(originalStyle);
|
|
|
|
|
+
|
|
|
|
|
+ if (layer._tooltip) {
|
|
|
|
|
+ layer.closeTooltip();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
}).addTo(this.townshipMapInstance);
|
|
}).addTo(this.townshipMapInstance);
|
|
|
|
|
|
|
|
// 添加GeoServer WMS图层作为底图
|
|
// 添加GeoServer WMS图层作为底图
|
|
|
- const wmsLayer = L.tileLayer.wms('http://localhost:8080/geoserver/soilgd/wms', {
|
|
|
|
|
- layers: 'soilgd:shapeMap', // 替换为你的图层名称
|
|
|
|
|
|
|
+ const wmsLayer = L.tileLayer.wms(this.getWmsUrl(), {
|
|
|
|
|
+ layers: this.getLayerName(), // 替换为你的图层名称
|
|
|
format: 'image/png',
|
|
format: 'image/png',
|
|
|
transparent: true,
|
|
transparent: true,
|
|
|
version: '1.1.0',
|
|
version: '1.1.0',
|
|
@@ -781,99 +849,162 @@ renderTownshipMap() {
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
-async handleWmsFeatureClick(e) {
|
|
|
|
|
- try {
|
|
|
|
|
- const { lat, lng } = e.latlng;
|
|
|
|
|
- const mapSize = this.townshipMapInstance.getSize();
|
|
|
|
|
-
|
|
|
|
|
- // 1. 转换坐标:地理坐标 → 地图像素坐标(Leaflet 默认 EPSG:4326)
|
|
|
|
|
- const containerPoint = this.townshipMapInstance.latLngToContainerPoint([lat, lng]);
|
|
|
|
|
- const x = Math.round(containerPoint.x);
|
|
|
|
|
- const y = Math.round(containerPoint.y);
|
|
|
|
|
-
|
|
|
|
|
- // 2. 构造 GetFeatureInfo 请求 URL(强制 JSON + 修正参数)
|
|
|
|
|
- const wmsBaseUrl = 'http://localhost:8080/geoserver/soilgd/wms'; // 确保工作区和服务名正确
|
|
|
|
|
- const params = new URLSearchParams({
|
|
|
|
|
- SERVICE: 'WMS',
|
|
|
|
|
- VERSION: '1.1.1', // 建议用 1.1.1(GeoServer 更兼容)
|
|
|
|
|
- REQUEST: 'GetFeatureInfo',
|
|
|
|
|
- LAYERS: 'soilgd:shapeMap',
|
|
|
|
|
- QUERY_LAYERS: 'soilgd:shapeMap',
|
|
|
|
|
- FORMAT: 'image/png',
|
|
|
|
|
- TRANSPARENT: 'true',
|
|
|
|
|
- INFO_FORMAT: 'application/json', // 强制返回 JSON
|
|
|
|
|
- bbox: this.townshipMapInstance.getBounds().toBBoxString(),
|
|
|
|
|
- X: x,
|
|
|
|
|
- Y: y,
|
|
|
|
|
- WIDTH: mapSize.x,
|
|
|
|
|
- HEIGHT: mapSize.y,
|
|
|
|
|
- SRS: 'EPSG:4326', // 与地图 CRS 一致
|
|
|
|
|
- FEATURE_COUNT: 1 // 限制返回 1 个要素(关键:确保日志中 FeatureCount=1)
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ async handleWmsFeatureClick(e) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const { lat, lng } = e.latlng;
|
|
|
|
|
+ const mapSize = this.townshipMapInstance.getSize();
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 转换坐标:地理坐标 → 地图像素坐标
|
|
|
|
|
+ const containerPoint = this.townshipMapInstance.latLngToContainerPoint([lat, lng]);
|
|
|
|
|
+ const x = Math.round(containerPoint.x);
|
|
|
|
|
+ const y = Math.round(containerPoint.y);
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 构造 GetFeatureInfo 请求 URL
|
|
|
|
|
+ const wmsBaseUrl = this.getWmsUrl();
|
|
|
|
|
+ const params = new URLSearchParams({
|
|
|
|
|
+ SERVICE: 'WMS',
|
|
|
|
|
+ VERSION: '1.1.1',
|
|
|
|
|
+ REQUEST: 'GetFeatureInfo',
|
|
|
|
|
+ LAYERS: this.getLayerName(),
|
|
|
|
|
+ QUERY_LAYERS: this.getLayerName(),
|
|
|
|
|
+ FORMAT: 'image/png',
|
|
|
|
|
+ TRANSPARENT: 'true',
|
|
|
|
|
+ INFO_FORMAT: 'application/json',
|
|
|
|
|
+ bbox: this.townshipMapInstance.getBounds().toBBoxString(),
|
|
|
|
|
+ X: x,
|
|
|
|
|
+ Y: y,
|
|
|
|
|
+ WIDTH: mapSize.x,
|
|
|
|
|
+ HEIGHT: mapSize.y,
|
|
|
|
|
+ SRS: 'EPSG:4326',
|
|
|
|
|
+ FEATURE_COUNT: 1
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- // 打印完整请求 URL(用于调试)
|
|
|
|
|
- const requestUrl = `${wmsBaseUrl}?${params.toString()}`;
|
|
|
|
|
- console.log('GetFeatureInfo 完整请求 URL:', requestUrl);
|
|
|
|
|
|
|
+ const requestUrl = `${wmsBaseUrl}?${params.toString()}`;
|
|
|
|
|
+ console.log('GetFeatureInfo 请求 URL:', requestUrl);
|
|
|
|
|
|
|
|
- // 3. 发送请求并解析 JSON
|
|
|
|
|
- const response = await fetch(requestUrl);
|
|
|
|
|
- if (!response.ok) throw new Error(`请求失败:${response.status} ${response.statusText}`);
|
|
|
|
|
|
|
+ // 3. 发送请求并解析 JSON
|
|
|
|
|
+ const response = await fetch(requestUrl);
|
|
|
|
|
+ if (!response.ok) throw new Error(`请求失败:${response.status} ${response.statusText}`);
|
|
|
|
|
|
|
|
- // 4. 验证响应类型(必须为 JSON)
|
|
|
|
|
- const contentType = response.headers.get('Content-Type');
|
|
|
|
|
- if (!contentType.includes('application/json')) {
|
|
|
|
|
- throw new Error(`非 JSON 响应:${contentType},请检查 GeoServer 配置`);
|
|
|
|
|
- }
|
|
|
|
|
- const featureData = await response.json();
|
|
|
|
|
|
|
+ const contentType = response.headers.get('Content-Type');
|
|
|
|
|
+ if (!contentType.includes('application/json')) {
|
|
|
|
|
+ throw new Error(`非 JSON 响应:${contentType}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const featureData = await response.json();
|
|
|
|
|
|
|
|
- if (!featureData?.features || !Array.isArray(featureData.features) || featureData.features.length === 0) {
|
|
|
|
|
- this.$message.warning('该区域无要素数据');
|
|
|
|
|
- return; // 无要素时直接返回,不继续处理
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!featureData?.features || !Array.isArray(featureData.features) || featureData.features.length === 0) {
|
|
|
|
|
+ this.$message.warning('该区域无要素数据');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const properties = featureData.features[0].properties || {};
|
|
|
|
|
- this.currentFeatureInfo = {
|
|
|
|
|
- avgmean: properties._avgmean, // 平均镉含量
|
|
|
|
|
- lncd: properties.Ln_Cd, // 输入镉通量
|
|
|
|
|
- outcd: properties.Out_Cd, // 输出镉通量
|
|
|
|
|
- netcd: properties.Net_Cd, // 净镉通量
|
|
|
|
|
- endcd: properties.End_Cd // 最终镉浓度
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ const properties = featureData.features[0].properties || {};
|
|
|
|
|
+ const parcelInfo = {
|
|
|
|
|
+ name: properties.ZLDWMC, //村名
|
|
|
|
|
+ avgmean: properties.CropCd_mean, // 平均镉含量
|
|
|
|
|
+ lncd: properties.In_Cd_mean, // 输入镉通量
|
|
|
|
|
+ outcd: properties.Out_Cd_mean, // 输出镉通量
|
|
|
|
|
+ netcd: properties.Net_Cd_mean, // 净镉通量
|
|
|
|
|
+ endcd: properties.End_Cd_mean // 最终镉浓度
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ console.log('获取到地块信息:', parcelInfo);
|
|
|
|
|
+
|
|
|
|
|
+ // 显示地块级tooltip
|
|
|
|
|
+ this.showParcelTooltip(e.latlng, parcelInfo);
|
|
|
|
|
+
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('WMS 要素点击交互失败:', error);
|
|
|
|
|
+ this.$message.warning('获取要素信息失败:' + error.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 修改 getCurrentMapScale 方法,确保准确计算
|
|
|
|
|
+ getCurrentMapScale() {
|
|
|
|
|
+ if (!this.townshipMapInstance) return 0;
|
|
|
|
|
|
|
|
- // 打印验证(可选)
|
|
|
|
|
- console.log('当前点击要素信息:', this.currentFeatureInfo);
|
|
|
|
|
- console.log('WMS 要素点击信息(JSON):', featureData);
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('WMS 要素点击交互失败:', error);
|
|
|
|
|
- this.$message.warning('获取要素信息失败:' + error.message);
|
|
|
|
|
- }
|
|
|
|
|
-},
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 获取地图中心点纬度
|
|
|
|
|
+ const center = this.townshipMapInstance.getCenter();
|
|
|
|
|
+ const lat = center.lat;
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当前缩放级别
|
|
|
|
|
+ const zoom = this.townshipMapInstance.getZoom();
|
|
|
|
|
+
|
|
|
|
|
+ // 更准确的比例尺计算公式
|
|
|
|
|
+ const metersPerPixel = (156543.03392 * Math.cos(lat * Math.PI / 180)) / Math.pow(2, zoom);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新当前比例尺状态
|
|
|
|
|
+ this.currentScale = metersPerPixel;
|
|
|
|
|
+
|
|
|
|
|
+ console.log(`当前比例尺: ${metersPerPixel.toFixed(2)} 米/像素, 缩放级别: ${zoom}`);
|
|
|
|
|
+ return metersPerPixel;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('计算比例尺失败:', error);
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 新增:处理缩放变化
|
|
|
|
|
+ handleZoomChange() {
|
|
|
|
|
+ const currentScale = this.getCurrentMapScale();
|
|
|
|
|
+ console.log(`缩放变化,当前比例尺: ${currentScale.toFixed(2)} 米/像素`);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果比例尺大于等于17.28米/像素,允许显示镇级tooltip
|
|
|
|
|
+ if (currentScale >= 17.28) {
|
|
|
|
|
+ console.log('比例尺合适,允许显示镇级tooltip');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果比例尺小于17.28米/像素,关闭所有打开的tooltip,准备显示地块级tooltip
|
|
|
|
|
+ console.log('比例尺过小,准备显示地块级tooltip');
|
|
|
|
|
+ this.closeAllTooltips();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 新增:关闭所有tooltip
|
|
|
|
|
+ closeAllTooltips() {
|
|
|
|
|
+ if (this.townshipMapInstance) {
|
|
|
|
|
+ // 遍历所有图层并关闭tooltip
|
|
|
|
|
+ this.townshipMapInstance.eachLayer((layer) => {
|
|
|
|
|
+ if (layer._tooltip) {
|
|
|
|
|
+ layer.closeTooltip();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // 新增:显示tooltip的方法
|
|
|
|
|
- showTooltip(layer, townName, latlng) {
|
|
|
|
|
- const riceData = this.dataMap[townName]; // 水稻Cd数据
|
|
|
|
|
- const fluxData = this.fluxDataMap[townName]; // 通量Cd数据
|
|
|
|
|
|
|
+ showTownshipTooltip(layer, townName, latlng) {
|
|
|
|
|
+ // 实时计算当前比例尺
|
|
|
|
|
+ const currentScale = this.getCurrentMapScale();
|
|
|
|
|
+ console.log(`尝试显示镇级tooltip,比例尺: ${currentScale.toFixed(2)} 米/像素`);
|
|
|
|
|
+
|
|
|
|
|
+ // 只有在比例尺大于等于17.28米/像素时才显示镇级tooltip
|
|
|
|
|
+ if (currentScale < 17.28) {
|
|
|
|
|
+ console.log('比例尺过小,不显示镇级tooltip');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- let tooltipContent = `<div class="town-tooltip"><h3>${townName}</h3>`;
|
|
|
|
|
|
|
+ const riceData = this.dataMap[townName];
|
|
|
|
|
+ const fluxData = this.fluxDataMap[townName];
|
|
|
|
|
+
|
|
|
|
|
+ let tooltipContent = `<div class="township-tooltip"><h3>${townName}</h3>`;
|
|
|
|
|
|
|
|
// 水稻Cd数据部分
|
|
// 水稻Cd数据部分
|
|
|
- tooltipContent += `<div style="margin-bottom: 10px;"><strong>水稻Cd</strong></div>`;
|
|
|
|
|
if (!riceData?.data?.基础统计) {
|
|
if (!riceData?.data?.基础统计) {
|
|
|
tooltipContent += '<p>水稻Cd数据加载中...</p>';
|
|
tooltipContent += '<p>水稻Cd数据加载中...</p>';
|
|
|
} else {
|
|
} else {
|
|
|
const stats = riceData.data.基础统计;
|
|
const stats = riceData.data.基础统计;
|
|
|
const dist = riceData.data.分布统计表格;
|
|
const dist = riceData.data.分布统计表格;
|
|
|
tooltipContent += `
|
|
tooltipContent += `
|
|
|
- <p><strong>样本数量:</strong> ${stats.采样点数量 ?? '无数据'}</p>
|
|
|
|
|
- <p><strong>平均值:</strong> ${stats.平均值?.toFixed(4) ?? '无数据'} mg/kg</p>
|
|
|
|
|
- <p><strong>安全区间占比:</strong> ${dist?.汇总?.安全区间占比 ?? '无'}</p>
|
|
|
|
|
- <p><strong>预警区间占比:</strong> ${dist?.汇总?.预警区间占比 ?? '无'}</p>
|
|
|
|
|
- <p><strong>超标区间占比:</strong> ${dist?.汇总?.超标区间占比 ?? '无'}</p>
|
|
|
|
|
|
|
+ <div class="tooltip-section">
|
|
|
|
|
+ <p><strong>水稻Cd含量:</strong> ${stats.平均值?.toFixed(4) ?? '无数据'} mg/kg</p>
|
|
|
|
|
+ <p><strong>安全区间占比:</strong> ${dist?.汇总?.安全区间占比 ?? '无'}%</p>
|
|
|
|
|
+ <p><strong>预警区间占比:</strong> ${dist?.汇总?.预警区间占比 ?? '无'}%</p>
|
|
|
|
|
+ <p><strong>超标区间占比:</strong> ${dist?.汇总?.超标区间占比 ?? '无'}%</p>
|
|
|
|
|
+ </div>
|
|
|
`;
|
|
`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 通量Cd数据部分
|
|
// 通量Cd数据部分
|
|
|
- tooltipContent += `<div style="margin-top: 10px; margin-bottom: 10px;"><strong>通量Cd</strong></div>`;
|
|
|
|
|
if (!fluxData) {
|
|
if (!fluxData) {
|
|
|
tooltipContent += '<p>通量Cd数据加载中...</p>';
|
|
tooltipContent += '<p>通量Cd数据加载中...</p>';
|
|
|
} else {
|
|
} else {
|
|
@@ -881,11 +1012,11 @@ async handleWmsFeatureClick(e) {
|
|
|
<p><strong>输入通量:</strong> ${fluxData.in_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.in_cd?.unit ?? ''}</p>
|
|
<p><strong>输入通量:</strong> ${fluxData.in_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.in_cd?.unit ?? ''}</p>
|
|
|
<p><strong>输出通量:</strong> ${fluxData.out_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.out_cd?.unit ?? ''}</p>
|
|
<p><strong>输出通量:</strong> ${fluxData.out_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.out_cd?.unit ?? ''}</p>
|
|
|
<p><strong>净通量:</strong> ${fluxData.net_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.net_cd?.unit ?? ''}</p>
|
|
<p><strong>净通量:</strong> ${fluxData.net_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.net_cd?.unit ?? ''}</p>
|
|
|
- <p><strong>土壤Cd浓度:</strong> ${fluxData.end_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.end_cd?.unit ?? ''}</p>
|
|
|
|
|
|
|
+ <p><strong>当年浓度:</strong> ${fluxData.end_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.end_cd?.unit ?? ''}</p>
|
|
|
`;
|
|
`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- tooltipContent += `</div>`;
|
|
|
|
|
|
|
+ tooltipContent += `</div></div>`;
|
|
|
|
|
|
|
|
// 先解除之前的tooltip绑定
|
|
// 先解除之前的tooltip绑定
|
|
|
if (layer._tooltip) {
|
|
if (layer._tooltip) {
|
|
@@ -894,18 +1025,59 @@ async handleWmsFeatureClick(e) {
|
|
|
|
|
|
|
|
// 使用Leaflet的tooltip
|
|
// 使用Leaflet的tooltip
|
|
|
layer.bindTooltip(tooltipContent, {
|
|
layer.bindTooltip(tooltipContent, {
|
|
|
- className: 'custom-town-tooltip',
|
|
|
|
|
|
|
+ className: 'custom-township-tooltip',
|
|
|
direction: 'top',
|
|
direction: 'top',
|
|
|
permanent: false,
|
|
permanent: false,
|
|
|
sticky: true,
|
|
sticky: true,
|
|
|
offset: [0, -10],
|
|
offset: [0, -10],
|
|
|
- interactive: false
|
|
|
|
|
|
|
+ interactive: true
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// 打开tooltip
|
|
// 打开tooltip
|
|
|
layer.openTooltip(latlng);
|
|
layer.openTooltip(latlng);
|
|
|
},
|
|
},
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 新增:显示地块级tooltip
|
|
|
|
|
+ showParcelTooltip(latlng, featureInfo) {
|
|
|
|
|
+ // 实时计算当前比例尺
|
|
|
|
|
+ const currentScale = this.getCurrentMapScale();
|
|
|
|
|
+ console.log(`尝试显示地块级tooltip,比例尺: ${currentScale.toFixed(2)} 米/像素`);
|
|
|
|
|
+
|
|
|
|
|
+ // 只有在比例尺小于17.28米/像素时才显示地块级tooltip
|
|
|
|
|
+ if (currentScale >= 17.28) {
|
|
|
|
|
+ console.log('比例尺过大,不显示地块级tooltip');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!featureInfo) {
|
|
|
|
|
+ console.log('无地块信息,不显示tooltip');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const tooltipContent = `
|
|
|
|
|
+ <div class="parcel-tooltip">
|
|
|
|
|
+ <h3>地块详细信息</h3>
|
|
|
|
|
+ <div class="tooltip-section">
|
|
|
|
|
+ <p><strong>村名:</strong>${featureInfo.name || '无数据'}</p>
|
|
|
|
|
+ <p><strong>水稻镉含量:</strong>${featureInfo.avgmean?.toFixed(4) || '无数据'} mg/kg</p>
|
|
|
|
|
+ <p><strong>镉输入通量:</strong>${featureInfo.lncd?.toFixed(4) || '无数据'} g/ha/a</p>
|
|
|
|
|
+ <p><strong>镉输出通量:</strong>${featureInfo.outcd?.toFixed(4) || '无数据'} g/ha/a</p>
|
|
|
|
|
+ <p><strong>镉净通量:</strong>${featureInfo.netcd?.toFixed(4) || '无数据'} g/ha/a</p>
|
|
|
|
|
+ <p><strong>当年镉浓度:</strong>${featureInfo.endcd?.toFixed(4) || '无数据'} g/ha/a</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ `;
|
|
|
|
|
+
|
|
|
|
|
+ // 关闭所有现有的tooltip
|
|
|
|
|
+ this.closeAllTooltips();
|
|
|
|
|
+
|
|
|
|
|
+ // 创建并显示popup
|
|
|
|
|
+ L.popup()
|
|
|
|
|
+ .setLatLng(latlng)
|
|
|
|
|
+ .setContent(tooltipContent)
|
|
|
|
|
+ .openOn(this.townshipMapInstance);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
// 上传并计算
|
|
// 上传并计算
|
|
|
async calculate() {
|
|
async calculate() {
|
|
|
if (!this.selectedFile) {
|
|
if (!this.selectedFile) {
|
|
@@ -1448,4 +1620,97 @@ async handleWmsFeatureClick(e) {
|
|
|
color: #0066CC;
|
|
color: #0066CC;
|
|
|
margin-right: 5px;
|
|
margin-right: 5px;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+/* 移除Leaflet点击要素时的黑色边框 */
|
|
|
|
|
+.township-map-container :deep(.leaflet-interactive:focus) {
|
|
|
|
|
+ outline: none !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.township-map-container :deep(.leaflet-interactive) {
|
|
|
|
|
+ outline: none !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 移除所有Leaflet要素的焦点轮廓 */
|
|
|
|
|
+:deep(.leaflet-container) {
|
|
|
|
|
+ outline: none !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.leaflet-clickable) {
|
|
|
|
|
+ outline: none !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 确保GeoJSON要素没有焦点样式 */
|
|
|
|
|
+:deep(.leaflet-geojson-layer) *:focus {
|
|
|
|
|
+ outline: none !important;
|
|
|
|
|
+}
|
|
|
|
|
+/* 镇级tooltip样式 */
|
|
|
|
|
+::v-deep .custom-township-tooltip {
|
|
|
|
|
+ background-color: rgba(255, 255, 255, 0.98) !important;
|
|
|
|
|
+ border: 2px solid #47C3B9 !important;
|
|
|
|
|
+ border-radius: 8px !important;
|
|
|
|
|
+ padding: 15px !important;
|
|
|
|
|
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
|
|
|
|
|
+ max-width: 350px !important;
|
|
|
|
|
+ font-size: 14px !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+::v-deep .custom-township-tooltip h3 {
|
|
|
|
|
+ color: #0066CC;
|
|
|
|
|
+ margin: 0 0 10px 0;
|
|
|
|
|
+ padding-bottom: 8px;
|
|
|
|
|
+ border-bottom: 2px solid #47C3B9;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+::v-deep .custom-township-tooltip .tooltip-section {
|
|
|
|
|
+ margin: 10px 0;
|
|
|
|
|
+ padding: 8px 0;
|
|
|
|
|
+ border-top: 1px solid #eee;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+::v-deep .custom-township-tooltip .tooltip-section h4 {
|
|
|
|
|
+ color: #47C3B9;
|
|
|
|
|
+ margin: 0 0 5px 0;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+::v-deep .custom-township-tooltip p {
|
|
|
|
|
+ margin: 3px 0;
|
|
|
|
|
+ line-height: 1.4;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 地块级popup样式 */
|
|
|
|
|
+::v-deep .leaflet-popup-content-wrapper {
|
|
|
|
|
+ background-color: rgba(255, 255, 255, 0.98) !important;
|
|
|
|
|
+ border-radius: 8px !important;
|
|
|
|
|
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+::v-deep .leaflet-popup-content {
|
|
|
|
|
+ margin: 15px !important;
|
|
|
|
|
+ font-size: 14px !important;
|
|
|
|
|
+ line-height: 1.4 !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.parcel-tooltip h3 {
|
|
|
|
|
+ color: #0066CC;
|
|
|
|
|
+ margin: 0 0 10px 0;
|
|
|
|
|
+ padding-bottom: 8px;
|
|
|
|
|
+ border-bottom: 2px solid #47C3B9;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tooltip-section {
|
|
|
|
|
+ margin: 10px 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tooltip-section p {
|
|
|
|
|
+ margin: 5px 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tooltip-section strong {
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ min-width: 100px;
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|