Commit 6cdc86e2 authored by daniel's avatar daniel

feat(map): 添加地图搜索功能

parent 09afcc3b
...@@ -30,6 +30,12 @@ ...@@ -30,6 +30,12 @@
<link href="https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css" rel="stylesheet"> <link href="https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css" rel="stylesheet">
<!-- 添加Mapbox GL JS --> <!-- 添加Mapbox GL JS -->
<script src="https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"></script> <script src="https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"></script>
<!-- 添加Mapbox GL Geocoder JS -->
<script
src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.1-dev/mapbox-gl-geocoder.min.js"></script>
<link rel="stylesheet"
href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.1-dev/mapbox-gl-geocoder.css"
type="text/css" />
<!-- Favicon--> <!-- Favicon-->
<link rel="shortcut icon" href="https://img.geexek.com/files/home/favicon.ico"> <link rel="shortcut icon" href="https://img.geexek.com/files/home/favicon.ico">
<!-- 自定义css --> <!-- 自定义css -->
...@@ -61,6 +67,12 @@ ...@@ -61,6 +67,12 @@
[v-cloak] { [v-cloak] {
display: none; display: none;
} }
/* 下移 50px,右移 10px(可根据需要调整) */
.mapboxgl-ctrl-top-left .mapboxgl-ctrl-geocoder {
margin-top: 90px;
/* margin-left: 10px; */
}
</style> </style>
</head> </head>
...@@ -81,7 +93,7 @@ ...@@ -81,7 +93,7 @@
<script type="text/x-template" id="my-app"> <script type="text/x-template" id="my-app">
<div style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; margin: 0; padding: 0;"> <div style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; margin: 0; padding: 0;">
<div id="map" style="width: 100%; height: 100%;z-index: 0;"></div> <div id="map" style="width: 100%; height: 100%;z-index: 0;"></div>
<div class="fixed left-0 top-0 z-10 flex flex-row justify-center items-center p-3"> <div class="fixed left-0 top-0 z-10 flex flex-col justify-center items-start gap-y-2 p-3">
<!-- <input <!-- <input
type="file" type="file"
accept=".gpx,.kml" accept=".gpx,.kml"
...@@ -105,6 +117,14 @@ ...@@ -105,6 +117,14 @@
<n-button @click="onBack" type="primary"> <n-button @click="onBack" type="primary">
返回 返回
</n-button> </n-button>
<n-switch v-model:value="isSatelliteLayer" @update:value="toggleMapLayer" >
<template #checked>
卫星地图
</template>
<template #unchecked>
普通地图
</template>
</n-switch>
</div> </div>
<div class="fixed right-3 top-0 z-10 p-3 flex flex-col gap-y-1"> <div class="fixed right-3 top-0 z-10 p-3 flex flex-col gap-y-1">
<div class="flex flex-row items-center mb-2 w-full gap-x-1 bg-white p-2 rounded-sm"> <div class="flex flex-row items-center mb-2 w-full gap-x-1 bg-white p-2 rounded-sm">
...@@ -483,68 +503,34 @@ ...@@ -483,68 +503,34 @@
color: "#7367F0" color: "#7367F0"
}, },
}; };
// 获取天地图的地址 // 获取天地图的地址
const getTDTUrls = () => { const getTDTUrls = (type = 'satellite') => {
const services = Array.from({ length: 7 }, (_, i) => i + 1); const services = Array.from({ length: 7 }, (_, i) => i + 1);
const baseUrl = 'https://t%s.tianditu.gov.cn'; const baseUrl = 'https://t%s.tianditu.gov.cn';
const generateUrls = (path) => { const generateUrls = (path) => {
return services.map((v) => baseUrl.replace('%s', v.toString()) + path); return services.map((v) => baseUrl.replace('%s', v.toString()) + path);
}; };
// 天地图卫星影像服务地址 // ===== 普通矢量图层(非卫星) =====
const normalPath = "/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=" + tk;
const TDTNormalUrls = generateUrls(normalPath);
// ===== 卫星图层 =====
const satellitePath = "/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=" + tk; const satellitePath = "/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=" + tk;
const TDTSatelliteUrls = generateUrls(satellitePath); const TDTSatelliteUrls = generateUrls(satellitePath);
// 获取原始地图数据集合 // ===== 注记层(通用) =====
const dataServerPath = "/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=" + tk;
const TDTDataServerUrls = generateUrls(dataServerPath);
// 天地图注记服务地址
const annotationPath = "/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=" + tk; const annotationPath = "/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=" + tk;
const TDTAnnotationUrls = generateUrls(annotationPath); const TDTAnnotationUrls = generateUrls(annotationPath);
return { return {
TDTNormalUrls,
TDTSatelliteUrls, TDTSatelliteUrls,
TDTDataServerUrls,
TDTAnnotationUrls, TDTAnnotationUrls,
}; };
} }
// mapbox的字体 // mapbox的字体
const glyphs = 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf'; const glyphs = 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf';
// 获取地址
const { TDTDataServerUrls, TDTAnnotationUrls, TDTSatelliteUrls } =
getTDTUrls();
// 构建样式
const defaultStyle = {
version: 8,
sources: {
tianmap: {
type: 'raster',
tiles: TDTSatelliteUrls,
tileSize: 256,
attribution: '天地图影像图',
},
// 添加注记图层的数据源
tianmapAnnotation: {
type: 'raster',
tiles: TDTAnnotationUrls,
tileSize: 256,
attribution: '天地图注记图层',
},
},
layers: [
{
id: 'tianmap-layer',
type: 'raster',
source: 'tianmap',
},
{
id: 'tianmap-annotation-layer',
type: 'raster',
source: 'tianmapAnnotation',
},
],
// 添加 glyphs 属性到地图样式
glyphs,
};
// 获取url参数 // 获取url参数
const getQueryParams = () => { const getQueryParams = () => {
...@@ -556,9 +542,10 @@ ...@@ -556,9 +542,10 @@
return params; return params;
} }
const urlParams = getQueryParams() const urlParams = getQueryParams()
</script> </script>
<script> <script>
const { createApp, defineComponent, ref, onMounted, reactive } = Vue; const { createApp, defineComponent, ref, onMounted, reactive, computed } = Vue;
const { useMessage, useNotification, dateZhCN, zhCN } = naive const { useMessage, useNotification, dateZhCN, zhCN } = naive
const MyApp = defineComponent({ const MyApp = defineComponent({
name: 'MyApp', name: 'MyApp',
...@@ -567,10 +554,75 @@ ...@@ -567,10 +554,75 @@
const loading = ref(false) const loading = ref(false)
const message = useMessage() const message = useMessage()
const notification = useNotification() const notification = useNotification()
onMounted(() => {
fetchData() // 是否是卫星地图图层
const isSatelliteLayer = ref(true)
// 获取地址
const { TDTNormalUrls, TDTSatelliteUrls, TDTAnnotationUrls } = getTDTUrls();
// 构建样式
const mapStyle = computed(() => ({
version: 8,
sources: {
tianmap: {
type: 'raster',
tiles: isSatelliteLayer.value ? TDTSatelliteUrls : TDTNormalUrls,
tileSize: 256,
attribution: isSatelliteLayer.value ? '天地图卫星影像图' : '天地图矢量图',
},
tianmapAnnotation: {
type: 'raster',
tiles: TDTAnnotationUrls,
tileSize: 256,
attribution: '天地图注记图层',
},
},
layers: [
{
id: 'tianmap-layer',
type: 'raster',
source: 'tianmap',
},
{
id: 'tianmap-annotation-layer',
type: 'raster',
source: 'tianmapAnnotation',
},
],
glyphs,
}));
// 添加搜索输入框
const geocoder = new MapboxGeocoder({
accessToken: mapBoxToken,
mapboxgl: mapboxgl,
placeholder: '输入地点搜索...',
marker: false // 不自动加 marker,可自行控制
}); });
onMounted(() => {
getCurrentPosition()
.then((position) => {
const { latitude, longitude } = position.coords;
GpsConfig.center = [longitude, latitude]
})
.catch((err) => {
console.warn("⚠️ 定位失败:", err.message);
})
.finally(() => {
console.log("📍 定位流程结束(成功或失败都会执行)");
fetchData()
});
});
// 获取定位信息
function getCurrentPosition(options = { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }) {
return new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject(new Error("浏览器不支持地理定位"));
return;
}
navigator.geolocation.getCurrentPosition(resolve, reject, options);
});
}
// 发起请求获取数据
const fetchData = async () => { const fetchData = async () => {
loading.value = true loading.value = true
let params = { let params = {
...@@ -600,6 +652,11 @@ ...@@ -600,6 +652,11 @@
/**------------------------ 地图初始化 start ------------------------**/ /**------------------------ 地图初始化 start ------------------------**/
// 地图对象 // 地图对象
let map = ref(null); let map = ref(null);
// 监听选中事件
geocoder.on('result', (e) => {
const { center, place_name } = e.result;
console.log('选中地点:', place_name, center);
});
// 初始化地图 // 初始化地图
const initMap = () => { const initMap = () => {
// 确保容器已完全渲染 // 确保容器已完全渲染
...@@ -608,7 +665,7 @@ ...@@ -608,7 +665,7 @@
if (!map.value) { if (!map.value) {
map.value = new mapboxgl.Map({ map.value = new mapboxgl.Map({
container: 'map', container: 'map',
style: defaultStyle, style: mapStyle.value,
container: 'map', container: 'map',
center: GpsConfig.map.center, center: GpsConfig.map.center,
zoom: GpsConfig.map.zoom, zoom: GpsConfig.map.zoom,
...@@ -946,6 +1003,8 @@ ...@@ -946,6 +1003,8 @@
if (!map.value) return; if (!map.value) return;
// 添加比例尺控件到地图 // 添加比例尺控件到地图
map.value.addControl(new mapboxgl.ScaleControl(), 'bottom-right'); map.value.addControl(new mapboxgl.ScaleControl(), 'bottom-right');
// 将搜索框添加到地图
map.value.addControl(geocoder, 'top-left');
}; };
/** /**
...@@ -1347,6 +1406,10 @@ ...@@ -1347,6 +1406,10 @@
// 回退 // 回退
window.history.back() window.history.back()
} }
// 切换底图
const toggleMapLayer = () => {
map.value.setStyle(mapStyle.value);
};
/**------------------------ marker end ------------------------**/ /**------------------------ marker end ------------------------**/
// 对外返回的信息 // 对外返回的信息
return { return {
...@@ -1371,6 +1434,8 @@ ...@@ -1371,6 +1434,8 @@
isAddMode, isAddMode,
fileList, fileList,
onBack, onBack,
isSatelliteLayer,
toggleMapLayer,
uploadUrl, uploadUrl,
uploadFinish uploadFinish
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment