492 lines
13 KiB
Vue
492 lines
13 KiB
Vue
<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>
|