本文将介绍openlayers中地图图层控制的组件制作方法,图层切换,显示隐藏,图层排序,在vue3中使用element,tree等组件。
(本专辑持续更新中)
1.基础知识准备
- 官方的图层组案例Layer Groups,首先看一下官方的图层控制是怎么回事。
- 官方的自定义控件的例子Custom Controls,了解自定义控件的用法。
在 OpenLayers 中,地图图层控制是一个常见的需求,包括图层的切换、显示隐藏、图层排序等。在 Vue 3 中结合 Element UI 或其他 UI 库(如 Ant Design Vue、Vuetify 等)来实现这些功能可以为用户提供一个友好的交互界面。
下面将介绍如何使用 OpenLayers、Vue 3 和 Element UI 来制作一个地图图层控制的组件。
1. 安装必要的库
首先,你需要在项目中安装 OpenLayers、Vue 3 和 Element UI(或其他 UI 库)。
npm install ol vue@next element-plus
2. 创建地图容器和图层控制组件
在你的 Vue 组件中,你可以创建一个地图容器和一个用于控制图层的组件。
<template> <div ref="mapContainer" class="map-container"></div> <el-tree :data="layersData" node-key="id" ref="layersTree" @check-change="handleCheckChange" show-checkbox ></el-tree> </template> <script> import { onMounted, ref } from 'vue'; import Map from 'ol/Map'; import View from 'ol/View'; import TileLayer from 'ol/layer/Tile'; import OSM from 'ol/source/OSM'; export default { name: 'MapComponent', setup() { const mapContainer = ref(null); const layersData = ref([ { id: 1, label: 'OpenStreetMap', visible: true }, // 其他图层数据 ]); let map; onMounted(() => { const osmLayer = new TileLayer({ source: new OSM(), }); map = new Map({ target: mapContainer.value, layers: [osmLayer], view: new View({ center: [0, 0], zoom: 2, }), }); }); function handleCheckChange(data, checked, indeterminate) { const layerId = data.id; // 根据图层 ID 控制图层的显示和隐藏 } return { mapContainer, layersData, handleCheckChange, }; }, }; </script> <style> .map-container { width: 100%; height: 400px; } </style>
3. 控制图层的显示和隐藏
在 handleCheckChange
函数中,你可以根据图层的 ID 来控制图层的显示和隐藏。你需要维护一个图层和 OpenLayers 图层对象的映射关系,然后在该函数中切换图层的可见性。
let layersMap = { 1: osmLayer, // 其他图层 }; function handleCheckChange(data, checked, indeterminate) { const layerId = data.id; const layer = layersMap[layerId]; if (layer) { layer.setVisible(checked); } }
4. 图层排序
图层排序可以通过调整 map.getLayers()
返回的图层数组的顺序来实现。你可以添加一个按钮来触发图层排序的操作。
<template> <!-- ... --> <el-button @click="sortLayers">排序图层</el-button> </template> <script> // ... export default { // ... methods: { sortLayers() { const layers = map.getLayers().getArray().slice(); // 对 layers 数组进行排序 layers.sort((a, b) => { // 根据你的排序规则进行比较 }); // 设置排序后的图层数组 map.getLayers().setArray(layers); }, }, }; </script>
5. 结合 Element UI 的 Tree 组件
在上面的例子中,已经使用了 Element UI 的 Tree 组件来展示图层,并通过 checkbox 来控制图层的显示和隐藏。你可以根据需要调整 Tree 组件的属性和事件来适应你的应用场景。
注意事项
- 确保 OpenLayers、Vue 3 和 Element UI 的版本兼容。
- 在实际项目中,可能需要处理更多的边界情况和性能优化。
- 如果使用的是 Vue 3 的 Composition API,注意正确地使用
ref
和reactive
来管理状态。 - 图层控制和排序的逻辑可能会根据你的具体需求而有所不同,上面的例子只是一个基本的实现。
2.正式开发
1. 这里使用天地图,以XYZ的方式引入。
function getTDXYZUrl(type: string) { return ( "http://t" + Math.round(Math.random() * 7) + ".tianditu.com/DataServer?T=" + type + "&x={x}&y={y}&l={z}&tk=" + myTDkey ); }
2. 图层的数据结构(模拟真实使用场景,方便图层的管理)。
const layersData = [ { id: "1", label: "天地图", url: "", opacity: 100, visible:true, children: [ { id: "4", label: "矢量注记", url: getTDXYZUrl("cva_w"), opacity: 100, visible:true, children: [], }, { id: "3", label: "矢量底图", url: getTDXYZUrl("vec_w"), opacity: 80, visible:true, children: [], }, ], }, { id: "2", label: "影像底图", url: getTDXYZUrl("img_w"), opacity: 100, visible:true, children: [], }, ];
3. 把图层数据转化成,openlayers的图层类。
function makeTree(layersData: Array<IlayersData>) { let rootLayers: (LayerGroup | TileLayer<XYZ>)[] = []; layersData.forEach((e) => { if (e.children.length > 0) { let group = new LayerGroup({ properties: { label: e.label, }, opacity: e.opacity!/100, layers: makeTree(e.children), visible:e.visible, }); rootLayers.unshift(group); } else { let layer = new TileLayer({ source: new XYZ({ url: e.url, projection: e.projection ?? "EPSG:3857", }), opacity: e.opacity!/100, properties: { label: e.label, }, }); rootLayers.unshift(layer) } }); return rootLayers } let layers = makeTree(layersData) function initMap(optons: { target: HTMLElement }) { return new Map({ layers: layers, target: optons.target as HTMLElement, view: new View({ center: [0, 0], zoom: 2, }), }); } export { initMap,layersData }
4. 控件的html
<div class="whr-vertical-control " ref="my-vertical-control"> <el-popover placement="left" :width="300" trigger="hover"> <template #reference> <!-- <el-tooltip class="box-item" effect="dark" content="图层控制" placement="top-start"> --> <el-button type="primary" icon="CopyDocument"/> <!-- </el-tooltip> --> </template> <template #default> <div> <div class="whr-popover-title"> <div class="yigeshu"></div> <div class="" style="margin-left: 5px;">图层控制</div> </div> <el-divider/> <div class="whr-popover-body"> <el-tree node-key="label" :data="layerTreeData" :props="defaultProps" @check-change="layerVisibleControl" :default-checked-keys="defaultCheckedLayers" show-checkbox> <template #default="{ node, data }"> <span class="custom-tree-node"> <span>{{ node.label }}</span> <span style="width: 100px;" v-if="data.children.length == 0"> <el-slider v-model="data.opacity" size="small" @change="layerOpacityChange($event,data)" /> </span> </span> </template> </el-tree> </div> </div> </template> </el-popover> </div>
5. 初始化控件,加载地图。
class MyVerticalControl extends Control { constructor(opt_options: any) { const options = opt_options || {}; super({ element: self.$refs["my-vertical-control"] as HTMLElement, target: options.target, }); } } this.vercontrol = new MyVerticalControl({}) this.map = initMap({target: this.$refs.map as HTMLElement}) (this.map as Map).addControl(this.vercontrol as Control)
如果需要地图组件保活,可以把添加控件写在activated钩子里。
activated() { (this.map as Map).addControl(this.vercontrol as Control); },
6. 控制图层显示隐藏和透明度,顺序也可以。
layerVisibleControl(data: Tree, checked: boolean, indeterminate: boolean) { console.log(data, checked, indeterminate) let currentLayer = this.map!.getAllLayers().filter(e => { return e.getProperties().label == data.label }) if (currentLayer.length > 0) { currentLayer[0].setVisible(checked) } }, layerOpacityChange($event,data){ console.log($event,data) let currentLayer = this.map!.getAllLayers().filter(e => { return e.getProperties().label == data.label }) if (currentLayer.length > 0) { currentLayer[0].setOpacity($event/100) } }