添加天地图底图,是通过addLayerToGroup()方式添加,而切换为mapbox底图(例如深色图,户外图)是使用setStyle()方法将地图样式设置为新样式。
在实践中发现这样直接切换,会使地图上原有的图层被移除,那么怎么实现mapbox地图与天地图底图相互切换,且保留地图上原有图层呢?
mapbox地图与天地图底图相互切换,且保留地图上原有图层
整体思路:
先定义图层组,在初始化底图时添加所有的图层组(占位图层),便于切换图层时,保证图层顺序。
切换地图样式(切换为mapbox底图)
使用传入的map对象和styleID参数,构建一个GET请求的URL,其中包含了访问地图样式所需的访问令牌(access token)。以此获取指定样式(例如mapbox://styles/mapbox/streets-v11)的数据。
使用axios库发送GET请求,并使用解构赋值将返回的响应数据中的data字段赋值给newStyle变量。
获取当前地图样式的配置信息,保存在currentStyle变量中。
将当前样式中的所有数据源(sources)复制到新样式的数据源中,以确保保留了原有的数据源。
查找需要保留在新样式中的图层的索引位置。
从当前样式中筛选出需要保留的应用程序图层(app layers)。这些图层是那些具有不同数据源设置的图层。
构建新的图层数组,按照以下顺序排列:将0到labelIndex之间的图层拷贝到新数组中,然后将所有图层拷贝到新数组中,最后将labelIndex到倒数第二个图层拷贝到新数组中。
使用map.setStyle(newStyle)方法将地图样式设置为新样式。
切换为天地图底图时
从当前样式中筛选出需要保留的图层,例如图层组中的图层,以及手动添加的其他图层
去除掉当前样式中不需要的属性
使用map.setStyle()方法将地图样式设置为新样式。
然后再使用addLayerToGroup()方式添加天地图底图
具体实现
1.首先定义图层组
需要注意,在给图层命名时需要便于筛选
例如命名时,名称都是“TD_BASE_XXXXXX”格式
const LAYERS_GROUP = {
TD_BASE_MAP: 'TD_BASE_MAP', //底图
TD_BASE_LABEL_MAP: 'TD_BASE_LABEL_MAP', //地图标注
};
2.初始化地图并添加所有的图层组(占位图层)
天地图图源
// 天地图影像图
const TD_IMG_URL = [
'https://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'https://t1.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'https://t2.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'https://t3.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'https://t4.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'https://t5.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'https://t6.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'https://t7.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
];
// 天地图标注图层
const TD_CIA_URL = [
'http://t0.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'http://t1.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'http://t2.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'http://t3.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'http://t4.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'http://t5.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'http://t6.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
'http://t7.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=你自己的天地图token',
];
给图层组添加图层方法
/**
* mapbox移除指定图层
* @param {string} layerId 图层id
*/
REMOVE_MAPBOX_LAYER(layerId, MAP) {
if (MAP.getLayer(layerId)) {
MAP.removeLayer(layerId);
MAP.removeSource(layerId);
}
}
/**
* mapbox切换底图
* @param { String } id 要切换的底图类型
*/
CHANGE_MAPBOX_BASE_LAYER = (MAP) => {
const layerId = LAYERS_GROUP.TD_BASE_MAP;
let sources;
// 移除原来图层
REMOVE_MAPBOX_LAYER(layerId, MAP);
sources = {
TD_IMG: {
id: layerId,
type: 'raster',
tiles: TD_IMG_URL,
tileSize: 256,
},
};
let layers = {
id: layerId,
type: 'raster',
source: sources.TD_IMG,
};
MAP.addLayerToGroup(layerId, layers);
};
/**
* mapbox切换标注
* @param { String } id 要切换的底图类型
*/
CHANGE_MAPBOX_BASE_LABEL_LAYER = (MAP) => {
const layerId = LAYERS_GROUP.TD_BASE_LABEL_MAP;
let sources;
// 移除原来图层
REMOVE_MAPBOX_LAYER(layerId, MAP);
sources = {
TD_IMG: {
id: layerId,
type: 'raster',
tiles: TD_CIA_URL,
tileSize: 256,
},
};
let layers = {
id: layerId,
type: 'raster',
source: sources.TD_IMG,
};
MAP.addLayerToGroup(layerId, layers);
};
地图初始化
mapInit() {
map2d = new MapboxGL.Map({
container: 'map',
style: {
sources: {},
version: 8,
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
layers: [],
},
antialias: true, // 启用抗锯齿以改善三维地形的渲染效果
});
const mapContainer = document.getElementById('map');
map2d.on('load', function () {
// 添加所有的图层组(占位图层)
_.map(LAYERS_GROUP, (id) => {
map2d.addLayerGroup(id);
});
// 添加底图
CHANGE_MAPBOX_BASE_LAYER(map2d);
CHANGE_MAPBOX_BASE_LABEL_LAYER(map2d);
});
return map2d;
}
进行到这一步以后,地图上显示天地图影像图以及标注
3.在现有地图上添加了其他图层(点,线,面)
注意,为了使切换底图时,方便过滤出这些图层,在给图层id命名时需要需要便于筛选
例如:“MAP_LAYER_XXXXXX”
此处对添加其他图层方法就不做赘述了
添加后的效果:
4.将现有地图切换为mapbox底图
获取当前地图样式的配置信息,保存在currentStyle变量中。
将当前样式中的所有数据源(sources)复制到新样式的数据源中,以确保保留了原有的数据源。
查找需要保留在新样式中的图层的索引位置。这里通过过滤掉newStyle.layers数组中id不包含’-label’的图层。为的是去掉mapbox的文字标注,因为我们这里使用的是天地图的标注。
从当前样式中筛选出需要保留的应用程序图层(app layers)。这里需要保留id中包含 ‘TD_BASE’ 或id中包含 ‘MAP_LAYER’ 的图层(这里就是前面说的命名时需要特别注意的原因)
这些图层是那些具有不同数据源设置的图层。
构建新的图层数组,然后将所有图层拷贝到新数组中
使用map.setStyle(newStyle)方法将地图样式设置为新样式。
/**
* 切换地图样式(mapbox底图)
* @param {*} data Mapbox地图样式
* @param {*} MAP 地图实例
*/
mapStyleSwitcher = (data, MAP) => {
let token = '你自己的mapbox 的 Token';
axios
.get(`https://api.mapbox.com/styles/v1/${data}?access_token=${token}`)
.then((result) => {
const { data: newStyle } = result;
const currentStyle = MAP.getStyle();
newStyle.sources = Object.assign({}, currentStyle.sources, newStyle.sources);
let Layers = newStyle.layers.filter((el) => {
//去掉mapbox底图地名标注
return !el.id.includes('-label');
});
const appLayers = currentStyle.layers.filter((el) => {
return (
(el.source &&
el.source != 'mapbox://mapbox.satellite' &&
el.source != 'mapbox' &&
el.source != 'composite') ||
el.id.includes('TD_BASE') ||
el.id.includes('MAP_LAYER')
);
});
Layers = [...Layers, ...appLayers];
newStyle.layers = Layers;
MAP.setStyle(newStyle);
})
.catch((error) => {
console.error(error);
});
};
调用方法:例如切换为mapbox深色图
mapStyleSwitcher('mapbox/dark-v11', map);
切换后的效果:
5.切换回天地图底图
从当前样式中筛选出需要保留的图层,例如图层组中的图层,以及手动添加的其他图层:依旧是需要保留id中包含 ‘TD_BASE’ 或id中包含 ‘MAP_LAYER’ 的图层
去除掉当前样式中不需要的属性
使用map.setStyle()方法将地图样式设置为新样式。
然后再使用addLayerToGroup()方式添加天地图底图
/**
* 重置地图样式(切换为天地图底图时使用)
* @param {*} MAP 地图实例
*/
mapStyleReset = (MAP) => {
const currentStyle = MAP.getStyle();
const appLayers = currentStyle.layers.filter((el) => {
return el.id.includes('TD_BASE') || el.id.includes('MAP_LAYER');
});
// console.log(appLayers);
let newStyle = {};
newStyle.layers = [...appLayers];
newStyle.glyphs = 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf';
newStyle.version = 8;
newStyle.sources = currentStyle.sources;
delete newStyle.sprite;
MAP.setStyle(newStyle);
};
调用方法:
mapStyleReset(map);
//重新添加底图到图层组中
CHANGE_MAPBOX_BASE_LAYER(map)
效果:
至此就实现了mapbox地图与天地图底图相互切换,且保留地图上原有图层的功能。
希望可以帮助遇到此类问题的朋友们提供到一些解决思路