airport-qingdao-vue3/src/components/map/OpenLayersMap.vue
2025-06-30 16:53:06 +08:00

492 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="map-container">
<div class="map-content-wrapper">
<div ref="mapContainer" id="map"></div>
</div>
<!-- 缩放控件 -->
<OpenLayersZoomControl
:map="map"
:resetView="resetView"
@compass="compass"
@zoomIn="zoomIn"
@zoomOut="zoomOut"
@layerChange="handleLayerChange"
/>
<!-- 地图信息 -->
<OpenLayersMapInfo
:map="map"
:mapConfig="mapConfig"
/>
<!-- 比例尺控件传递map对象 -->
<OpenLayersScaleControl :map="map" />
<!-- 路线绘制控件 -->
<RouteDrawControl :map="map" ref="routeDrawControlRef" v-if="map" />
<!-- 插槽允许父组件添加其他控件 -->
<slot></slot>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import TileGrid from "ol/tilegrid/TileGrid";
import { TileSuperMapRest } from "@supermap/iclient-ol";
import proj4 from "proj4";
import { register } from "ol/proj/proj4";
import { get as getProjection } from "ol/proj";
import { defaults as defaultInteractions } from "ol/interaction";
import { MouseWheelZoom } from "ol/interaction";
import OpenLayersZoomControl from "./controls/OpenLayersZoomControl.vue";
import OpenLayersMapInfo from "./controls/OpenLayersMapInfo.vue";
import OverviewMap from 'ol/control/OverviewMap';
import ScaleLine from 'ol/control/ScaleLine';
import OpenLayersScaleControl from "./controls/OpenLayersScaleControl.vue";
import RouteDrawControl from "./controls/RouteDrawControl.vue";
// 注册 EPSG:4528 投影
proj4.defs(
"EPSG:4528",
"+proj=tmerc +lat_0=0 +lon_0=120 +k=1 +x_0=40500000 +y_0=0 +ellps=GRS80 +units=m +no_defs"
);
register(proj4);
const projection = getProjection("EPSG:4528");
const bounds = [
4.05027047458807e7, // left
4019271.0092735, // bottom
4.0514081829214096e7, // right
4030939.1342735, // top
];
const center = [4.0512494329214066e7, 4026228.757190162];
const resolutions = [
1322.9166666666665, 529.1666666666666, 264.5833333333333, 132.29166666666666,
66.14583333333333, 26.45833333333333, 13.229166666666664, 6.614583333333332,
2.645833333333333, 1.3229166666666665, 0.5291666666666666, 0.2645833333333333,
0.13229166666666664,
];
// 设置旋转角度
const rotation = -1.26; // 逆时针旋转约72度
const mapContainer = ref(null);
const map = ref(null); // 使用ref使地图实例成为响应式
// 设置缩放级别范围
const minZoom = 7; // 最小缩放级别
const maxZoom = 15; // 最大缩放级别
const initialZoom = 8; // 设置为7.5在7和8之间
onMounted(() => {
// 检查是否有保存的默认视图
let savedView = null;
try {
const savedViewString = localStorage.getItem('defaultMapView');
if (savedViewString) {
savedView = JSON.parse(savedViewString);
console.log('加载保存的默认视图:', savedView);
}
} catch (error) {
console.error('加载保存的视图出错:', error);
}
// 创建TileGrid
const tileGrid = new TileGrid({
extent: bounds,
resolutions: resolutions,
tileSize: 256,
origin: [bounds[0], bounds[3]], // 重要:使用左上角为原点
});
// 创建地图视图,优先使用保存的视图设置
const view = new View({
projection: projection,
center: savedView ? savedView.center : center,
zoom: savedView ? savedView.zoom : initialZoom,
minZoom: minZoom,
maxZoom: maxZoom,
resolutions: resolutions,
rotation: savedView ? savedView.rotation : rotation, // 使用保存的旋转角度或默认值
});
// 创建地图,与.ol3页面完全一致
map.value = new Map({
target: mapContainer.value,
controls: [], // 禁用所有默认控件,包括版权信息
loadTilesWhileAnimating: true, // 在动画期间加载瓦片
interactions: defaultInteractions({
altShiftDragRotate: false, // 禁用Alt+Shift拖动旋转
pinchRotate: false, // 禁用捏合旋转
}).extend([
// 自定义鼠标滚轮交互,保持旋转角度
new MouseWheelZoom({
constrainResolution: true,
}),
]),
layers: [
// 使用TileLayer包裹TileSuperMapRest
new TileLayer({
source: new TileSuperMapRest({
url: "http://10.96.3.10:8090/iserver/services/map-QDJC_DT-GX3/rest/maps/QDJC2DZT",
projection: "EPSG:4528",
tileGrid: tileGrid,
wrapX: false,
attributions: "", // 空字符串移除属性信息
}),
}),
],
view: view,
});
console.log('地图实例已创建:', map.value);
// 创建鹰眼图层
const overviewLayer = new TileLayer({
source: new TileSuperMapRest({
url: "http://10.96.3.10:8090/iserver/services/map-QDJC_DT-GX3/rest/maps/QDJC2DZT",
projection: "EPSG:4528",
tileGrid: tileGrid,
wrapX: false
})
});
// 创建鹰眼控件
const overviewMapControl = new OverviewMap({
className: 'ol-overviewmap custom-overview', // 用于自定义样式
layers: [overviewLayer],
collapsed: false,
collapsible: false,
rotateWithView: true,
tipLabel: '', // 移除提示文本
// 新增view属性设置鹰眼内部地图的缩放级别
view: new View({
projection: projection,
center: center,
zoom: 10, // 你可以根据需要调整这个值,数字越大越放大
minZoom: 3,
maxZoom: 15,
resolutions: resolutions
})
});
// 添加控件到地图
map.value.addControl(overviewMapControl);
// 确保鹰眼图正确显示
setTimeout(() => {
const overviewElement = document.querySelector('.custom-overview');
if (overviewElement) {
overviewElement.style.display = 'block';
console.log('鹰眼图已添加到地图');
} else {
console.warn('未找到鹰眼图元素');
}
}, 500);
// 确保地图完全加载后再次调整视图
setTimeout(() => {
// 使用fit方法让地图内容居中显示
map.value.getView().fit(bounds, {
size: map.value.getSize(),
padding: [50, 50, 50, 50], // 添加内边距,确保内容不贴边
maxZoom: 15,
});
// 应用旋转确保rotation设置有效
map.value.getView().animate({
rotation: rotation,
duration: 500,
});
// 让地图重新计算尺寸,确保正确显示
map.value.updateSize();
}, 200);
// 自适应拖拽方向
// 监听地图旋转变化,自动调整拖拽方向
view.on("change:rotation", function () {
// 当地图旋转时不需要额外操作OpenLayers内部会自动处理拖拽方向
// 但我们可以在这里做一些自定义处理比如更新UI等
});
});
onUnmounted(() => {
if (map.value) {
map.value.setTarget(null);
}
});
// 供子组件调用的地图操作方法
function zoomIn() {
if (map.value) {
map.value.getView().setZoom(map.value.getView().getZoom() + 0.3);
}
}
function zoomOut() {
if (map.value) {
map.value.getView().setZoom(map.value.getView().getZoom() - 0.3);
}
}
function compass() {
if (map.value) {
// 重置旋转和中心
map.value.getView().setRotation(rotation);
map.value.getView().setCenter(center);
map.value.getView().setZoom(initialZoom);
}
}
// 可选:重置视图方法,供控件调用
function resetView() {
if (map.value) {
// 检查是否有保存的默认视图
try {
const savedViewString = localStorage.getItem('defaultMapView');
if (savedViewString) {
const savedView = JSON.parse(savedViewString);
map.value.getView().setCenter(savedView.center);
map.value.getView().setZoom(savedView.zoom);
map.value.getView().setRotation(savedView.rotation);
return;
}
} catch (error) {
console.error('加载保存的视图出错:', error);
}
// 如果没有保存的视图,使用默认值
map.value.getView().setRotation(rotation);
map.value.getView().setCenter(center);
map.value.getView().setZoom(initialZoom);
}
}
// 地图配置信息传递给MapInfo组件
const mapConfig = {
center,
rotation,
initialZoom,
bounds: {
left: bounds[0],
bottom: bounds[1],
right: bounds[2],
top: bounds[3]
}
};
// 图层切换处理函数
function handleLayerChange(layerType) {
console.log('切换图层类型:', layerType);
// 这里可以实现图层切换逻辑
}
// 添加一个方法来获取当前地图状态
function getCurrentMapState() {
if (map.value) {
const view = map.value.getView();
const currentCenter = view.getCenter();
const currentZoom = view.getZoom();
const currentRotation = view.getRotation();
console.log('当前地图状态:');
console.log('中心点:', currentCenter);
console.log('缩放级别:', currentZoom);
console.log('旋转角度:', currentRotation);
return {
center: currentCenter,
zoom: currentZoom,
rotation: currentRotation
};
}
return null;
}
// 保存当前视图为默认视图
function saveCurrentView() {
const currentState = getCurrentMapState();
if (currentState) {
// 这里可以将当前状态保存到localStorage或后端
localStorage.setItem('defaultMapView', JSON.stringify(currentState));
alert('当前视图已保存为默认视图');
// 更新mapConfig以便其他组件使用
mapConfig.center = currentState.center;
mapConfig.initialZoom = currentState.zoom;
mapConfig.rotation = currentState.rotation;
}
}
// 打印当前视图状态
function printCurrentView() {
const currentState = getCurrentMapState();
if (currentState) {
console.log('%c当前视图状态 - 复制这些值到代码中:', 'background: #4CAF50; color: white; padding: 4px; border-radius: 4px;');
console.log(`const center = [${currentState.center[0]}, ${currentState.center[1]}];`);
console.log(`const initialZoom = ${currentState.zoom};`);
console.log(`const rotation = ${currentState.rotation};`);
alert(`当前视图状态已打印到控制台。\n\n中心点: [${currentState.center[0].toFixed(2)}, ${currentState.center[1].toFixed(2)}]\n缩放级别: ${currentState.zoom.toFixed(2)}\n旋转角度: ${currentState.rotation.toFixed(2)}`);
}
}
// 暴露方法给父组件使用
defineExpose({
zoomIn,
zoomOut,
compass,
resetView,
getCurrentMapState // 暴露获取当前地图状态的方法
});
</script>
<style scoped>
.map-container {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
display: flex; /* 使用flex布局 */
flex-direction: column;
background-color: #eeece1;
}
.map-content-wrapper {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
flex: 1; /* 填充剩余空间 */
background-color: #eeece1;
}
#map {
width: 100%;
height: 100%;
background-color: #eeece1;
z-index: 1;
overflow: visible !important; /* 允许内容溢出 */
will-change: transform;
}
/* 确保OpenLayers内部元素正确显示 */
:deep(.ol-viewport) {
background-color: #eeece1 !important;
}
/* 隐藏所有OpenLayers控件但保留小地图 */
:deep(.ol-control:not(.custom-overview)) {
display: none !important;
}
/* 隐藏OpenLayers属性信息 */
:deep(.ol-attribution) {
display: none !important;
}
:deep(.custom-overview) {
position: absolute;
bottom: 20px;
left: 20px;
background-color: rgba(255, 255, 255, 0.7);
border-radius: 4px;
padding: 2px;
border: 1px solid #ccc;
display: block !important; /* 强制显示 */
z-index: 1000; /* 确保在其他元素之上 */
}
:deep(.custom-overview .ol-overviewmap-map) {
width: 160px !important;
height: 160px !important;
}
:deep(.custom-overview button) {
display: none;
}
:deep(.custom-overview .ol-overviewmap-box) {
border: 2px solid #1869BD;
}
:deep(.custom-scale-line) {
position: absolute;
left: 30px;
bottom: 190px;
background: rgba(255,255,255,0.8);
border-radius: 2px;
padding: 2px 8px;
font-size: 12px;
color: #27313F;
z-index: 1200;
font-family: Arial, sans-serif;
}
.save-view-btn {
position: absolute;
top: 20px;
right: 20px;
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px 12px;
font-size: 14px;
color: #333;
cursor: pointer;
z-index: 3000;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.save-view-btn:hover {
background-color: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
/* 响应式调整 */
@media (max-width: 768px) {
.save-view-btn {
padding: 6px 10px;
font-size: 12px;
top: 10px;
right: 10px;
}
}
.print-view-btn {
position: absolute;
top: 20px;
right: 140px;
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px 12px;
font-size: 14px;
color: #333;
cursor: pointer;
z-index: 3000;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.print-view-btn:hover {
background-color: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
/* 响应式调整 */
@media (max-width: 768px) {
.print-view-btn {
padding: 6px 10px;
font-size: 12px;
top: 10px;
right: 120px;
}
}
</style>