完成页面的基础配置
This commit is contained in:
250
src/app/OlMap/Controls/BaseLayers.tsx
Normal file
250
src/app/OlMap/Controls/BaseLayers.tsx
Normal file
@@ -0,0 +1,250 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useMap } from "../MapComponent";
|
||||
import TileLayer from "ol/layer/Tile.js";
|
||||
import XYZ from "ol/source/XYZ.js";
|
||||
import mapboxOutdoors from "@assets/map/layers/mapbox-outdoors.png";
|
||||
import mapboxSatellite from "@assets/map/layers/mapbox-satellite.png";
|
||||
import mapboxSatelliteStreet from "@assets/map/layers/mapbox-satellite-streets.png";
|
||||
import mapboxStreets from "@assets/map/layers/mapbox-streets.png";
|
||||
import clsx from "clsx";
|
||||
import Group from "ol/layer/Group";
|
||||
import { MAPBOX_TOKEN } from "@config/config";
|
||||
import { TIANDITU_TOKEN } from "@config/config";
|
||||
const INITIAL_LAYER = "mapbox-outdoors";
|
||||
|
||||
const streetsLayer = new TileLayer({
|
||||
source: new XYZ({
|
||||
url: `https://api.mapbox.com/styles/v1/mapbox/streets-v12/tiles/256/{z}/{x}/{y}@2x?access_token=${MAPBOX_TOKEN}`,
|
||||
tileSize: 512,
|
||||
maxZoom: 20,
|
||||
projection: "EPSG:3857",
|
||||
attributions:
|
||||
'数据来源:<a href="https://www.mapbox.com/">Mapbox</a> & <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
|
||||
}),
|
||||
});
|
||||
const outdoorsLayer = new TileLayer({
|
||||
source: new XYZ({
|
||||
url: `https://api.mapbox.com/styles/v1/mapbox/outdoors-v12/tiles/256/{z}/{x}/{y}@2x?access_token=${MAPBOX_TOKEN}`,
|
||||
tileSize: 512,
|
||||
maxZoom: 20,
|
||||
projection: "EPSG:3857",
|
||||
attributions:
|
||||
'数据来源:<a href="https://www.mapbox.com/">Mapbox</a> & <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
|
||||
}),
|
||||
});
|
||||
const satelliteLayer = new TileLayer({
|
||||
source: new XYZ({
|
||||
url: `https://api.mapbox.com/styles/v1/mapbox/satellite-v9/tiles/256/{z}/{x}/{y}@2x?access_token=${MAPBOX_TOKEN}`,
|
||||
tileSize: 512,
|
||||
maxZoom: 20,
|
||||
projection: "EPSG:3857",
|
||||
attributions:
|
||||
'数据来源:<a href="https://www.mapbox.com/">Mapbox</a> & <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
|
||||
}),
|
||||
});
|
||||
const satelliteStreetsLayer = new TileLayer({
|
||||
source: new XYZ({
|
||||
url: `https://api.mapbox.com/styles/v1/mapbox/satellite-streets-v12/tiles/256/{z}/{x}/{y}@2x?access_token=${MAPBOX_TOKEN}`,
|
||||
tileSize: 512,
|
||||
maxZoom: 20,
|
||||
projection: "EPSG:3857",
|
||||
attributions:
|
||||
'数据来源:<a href="https://www.mapbox.com/">Mapbox</a> & <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
|
||||
}),
|
||||
});
|
||||
|
||||
const tiandituVectorLayer = new TileLayer({
|
||||
source: new XYZ({
|
||||
url: `https://t0.tianditu.gov.cn/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=${TIANDITU_TOKEN}`,
|
||||
projection: "EPSG:3857",
|
||||
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
|
||||
}),
|
||||
});
|
||||
const tiandituVectorAnnotationLayer = new TileLayer({
|
||||
source: new XYZ({
|
||||
url: `https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
|
||||
projection: "EPSG:3857",
|
||||
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
|
||||
}),
|
||||
});
|
||||
const tiandituImageLayer = new TileLayer({
|
||||
source: new XYZ({
|
||||
url: `https://t0.tianditu.gov.cn/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=${TIANDITU_TOKEN}`,
|
||||
projection: "EPSG:3857",
|
||||
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
|
||||
}),
|
||||
});
|
||||
const tiandituImageAnnotationLayer = new TileLayer({
|
||||
source: new XYZ({
|
||||
url: `https://t0.tianditu.gov.cn/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=${TIANDITU_TOKEN}`,
|
||||
projection: "EPSG:3857",
|
||||
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
|
||||
}),
|
||||
});
|
||||
const tiandituVectorLayerGroup = new Group({
|
||||
layers: [tiandituVectorLayer, tiandituVectorAnnotationLayer],
|
||||
});
|
||||
const tiandituImageLayerGroup = new Group({
|
||||
layers: [tiandituImageLayer, tiandituImageAnnotationLayer],
|
||||
});
|
||||
const baseLayers = [
|
||||
{
|
||||
id: "mapbox-outdoors",
|
||||
name: "户外地图",
|
||||
layer: outdoorsLayer,
|
||||
// layer: tiandituVectorLayerGroup,
|
||||
img: mapboxOutdoors.src,
|
||||
},
|
||||
{
|
||||
id: "mapbox-satellite",
|
||||
name: "卫星地图",
|
||||
layer: satelliteLayer,
|
||||
// layer: tiandituImageLayerGroup,
|
||||
img: mapboxSatellite.src,
|
||||
},
|
||||
{
|
||||
id: "mapbox-satellite-streets",
|
||||
name: "卫星街道地图",
|
||||
layer: satelliteStreetsLayer,
|
||||
img: mapboxSatelliteStreet.src,
|
||||
},
|
||||
{
|
||||
id: "mapbox-streets",
|
||||
name: "街道地图",
|
||||
layer: streetsLayer,
|
||||
img: mapboxStreets.src,
|
||||
},
|
||||
];
|
||||
|
||||
const BaseLayers: React.FC = () => {
|
||||
const map = useMap();
|
||||
// 切换底图选项展开,控制显示和卸载
|
||||
const [isShow, setShow] = useState(false);
|
||||
const [isExpanded, setExpanded] = useState(false);
|
||||
// 快速切换底图
|
||||
const [activeId, setActiveId] = useState(INITIAL_LAYER);
|
||||
|
||||
// 初始化默认底图
|
||||
useEffect(() => {
|
||||
if (!map) return;
|
||||
// 添加所有底图至地图并根据 activeId 控制可见性
|
||||
baseLayers.forEach((layerInfo) => {
|
||||
const layers = map.getLayers().getArray();
|
||||
if (!layers.includes(layerInfo.layer)) {
|
||||
map.getLayers().insertAt(0, layerInfo.layer);
|
||||
}
|
||||
layerInfo.layer.setVisible(layerInfo.id === activeId);
|
||||
});
|
||||
}, [map]);
|
||||
|
||||
const changeMapLayers = (id: string) => {
|
||||
if (map) {
|
||||
// 根据 id 设置每个图层的可见性
|
||||
baseLayers.forEach(({ id: lid, layer }) => {
|
||||
layer.setVisible(lid === id);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleQuickSwitch = () => {
|
||||
const nextId =
|
||||
activeId === baseLayers[0].id ? baseLayers[1].id : baseLayers[0].id;
|
||||
setActiveId(nextId);
|
||||
handleMapLayers(nextId);
|
||||
};
|
||||
|
||||
const handleMapLayers = (id: string) => {
|
||||
setActiveId(id);
|
||||
changeMapLayers(id);
|
||||
};
|
||||
|
||||
// 记录定时器,避免多次触发
|
||||
const hideTimer = React.useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const handleEnter = () => {
|
||||
if (hideTimer.current) {
|
||||
clearTimeout(hideTimer.current);
|
||||
hideTimer.current = null;
|
||||
}
|
||||
setShow(true);
|
||||
setExpanded(true);
|
||||
};
|
||||
|
||||
const handleLeave = () => {
|
||||
setShow(false);
|
||||
hideTimer.current = setTimeout(() => {
|
||||
setExpanded(false);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute right-17 bottom-8">
|
||||
<div
|
||||
className="w-20 h-20 bg-white rounded-xl drop-shadow-xl shadow-black"
|
||||
onMouseEnter={handleEnter}
|
||||
onMouseLeave={handleLeave}
|
||||
>
|
||||
<div className="w-20 h-20 p-1">
|
||||
<button onClick={() => handleQuickSwitch()}>
|
||||
<img
|
||||
width={240}
|
||||
height={100}
|
||||
src={
|
||||
activeId === baseLayers[0].id
|
||||
? baseLayers[1].img
|
||||
: baseLayers[0].img
|
||||
}
|
||||
alt={
|
||||
activeId === baseLayers[0].id
|
||||
? baseLayers[1].name
|
||||
: baseLayers[0].name
|
||||
}
|
||||
className="object-cover object-left w-18 h-18 rounded-xl"
|
||||
/>
|
||||
<div className=" absolute left-1 bottom-1 flex w-18 h-auto items-center justify-center rounded-b-xl text-xs text-white bg-black opacity-80">
|
||||
<span>
|
||||
{activeId === baseLayers[0].id
|
||||
? baseLayers[1].name
|
||||
: baseLayers[0].name}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{isExpanded && (
|
||||
<div
|
||||
className={clsx(
|
||||
"absolute flex right-24 bottom-0 w-90 h-25 bg-white rounded-xl drop-shadow-xl shadow-black transition-all duration-300",
|
||||
isShow ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
onMouseEnter={handleEnter}
|
||||
onMouseLeave={handleLeave}
|
||||
>
|
||||
{baseLayers.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
className="flex flex-auto flex-col justify-center items-center text-gray-500 text-xs"
|
||||
onClick={() => handleMapLayers(item.id)}
|
||||
>
|
||||
<img
|
||||
width={240}
|
||||
height={100}
|
||||
src={item.img}
|
||||
alt={item.name}
|
||||
className={clsx(
|
||||
"object-cover object-left w-16 h-16 rounded-md border-2 border-white hover:ring-2 ring-blue-300",
|
||||
{
|
||||
"ring-1 ring-blue-300": activeId === item.id,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
<span className="pt-1">{item.name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BaseLayers;
|
||||
Reference in New Issue
Block a user