(带源码)基于Vue3+OpenLayers实现的多图层切换

前言

​ 这篇文章是个人学习的总结,由于本人技术能力有限,如有不足之处,欢迎大家批评指正。这篇文章是个人学习的总结,由于本人技术能力有限,如有不足之处,欢迎大家批评指正
演示图
请添加图片描述

创建地图实例

如何创建Vue项目和导入相关依赖这里不再赘述,我这个初始化用的是Bing地图,如果你没有Bing密钥可以用高德或者天地图都可以

baseMap.vue
<template>
  <div>
    <div id="map" ref="mapContainer"></div>
  </div>
</template>

<script>
import {onMounted, ref} from 'vue';
import "ol/ol.css";
import Map from 'ol/Map';
import TileLayer from 'ol/layer/Tile';
import BingMaps from 'ol/source/BingMaps';
import View from 'ol/View';

export default {
  name: "baseMap",
  setup() {
    const mapContainer = ref(null); // 使用 ref 创建响应式引用
    const mapInstance = ref(null);  // 存储地图实例的引用
    // 初始化地图的方法
    const initMap = () => {
      const bingMapsKey = '你的必应地图Key';
      const bingMapLayer = new TileLayer({
        visible: true,
        source: new BingMaps({
          key: bingMapsKey,
          imagerySet: 'Aerial',
        }),
      });
      // 创建地图实例
      mapInstance.value = new Map({
        target: mapContainer.value, // 使用 ref 引用的 DOM 元素作为目标
        layers: [bingMapLayer],
        view: new View({
          center: [0, 0], // 根据需要设置中心
          zoom: 2,        // 根据需要设置缩放级别
        }),
      });
    };
    // 组件挂载后初始化地图
    onMounted(initMap);
    // 返回在模板中需要的变量
    return {
      mapContainer,
      mapInstance
    };
  }
};
</script>
<style scoped>
#map {
  width: 100%;
  height: 100vh;
}
</style>

map我是用的全局变量,可能有的项目是基于vue2版本,到时候可以把变量放到data里面

**注意:**map一定要设置高度,否则可能会加载失败,导致地图无法显示

下一步添加图层组

mapLayers.js

首先导入所需openlayers模块

import TileLayer from "ol/layer/Tile";
import {BingMaps} from "ol/source";
import XYZ from "ol/source/XYZ";
import OSM from "ol/source/OSM";

然后把上一步的bing地图放到这个js文件中,再添加几个map

const bingMapsKey = '你的密钥';
// 创建Bing地图瓦片图层
const bingMapLayer = new TileLayer({
    visible: true, // 设置图层可见
    key: 'bingMapLayer',
    source: new BingMaps({
        key: bingMapsKey, // 设置API密钥
        imagerySet: 'Aerial', // 设置卫星图像
        crossOrigin: "anonymous"
    }),
});
const satelliteLayer = new TileLayer({
    visible: false, // 设置图层可见
    key: 'satelliteLayer',
    source: new XYZ({
        url: 'http://t{0-7}.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=7786923a385369346d56b966bb6ad62f',
        crossOrigin: "anonymous"
    })
});
const satelliteLabelLayer = new TileLayer({
    visible: false, // 设置图层可见
    key: 'satelliteLabelLayer',
    source: new XYZ({
        url: 'http://t{0-7}.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=7786923a385369346d56b966bb6ad62f',
        crossOrigin: "anonymous"
    })
});

const streetLayer = new TileLayer({
    visible: false, // 设置图层可见
    key: 'streetLayer',
    source: new XYZ({
        url: 'http://t{0-7}.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=7786923a385369346d56b966bb6ad62f',
        crossOrigin: "anonymous"
    })
});
const streetLabelLayer = new TileLayer({
    visible: false, // 设置图层可见
    key: 'streetLabelLayer',
    source: new XYZ({
        url: 'http://t{0-7}.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=7786923a385369346d56b966bb6ad62f',
        crossOrigin: "anonymous"
    })
});
const terLayer = new TileLayer({
    visible: false, // 设置图层可见
    key: 'terLayer',
    source: new XYZ({
        url: 'http://t{0-7}.tianditu.com/DataServer?T=ter_w&x={x}&y={y}&l={z}&tk=7786923a385369346d56b966bb6ad62f',
        crossOrigin: "anonymous"
    })
});
const osmLayer = new TileLayer({
    key: 'osmLayer',
    visible: false, // 设置图层可见
    source: new OSM()
});

const arcgisImgLayer = new TileLayer({//arcgis影像
    visible: false,
    key: 'arcgisImgLayer',
    source: new XYZ({
        url: 'https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
        crossOrigin: "anonymous"
    })
});
const arcgisVecLayer = new TileLayer({//arcgis街道
    visible: false,
    key: 'arcgisVecLayer',
    source: new XYZ({
        url: 'http://cache1.arcgisonline.cn/arcgis/rest/services/ChinaOnlineCommunity/MapServer/tile/{z}/{y}/{x}',
        crossOrigin: "anonymous"
    })
});
const googleImgLayer = new TileLayer({//谷歌影像
    visible: false,
    key: 'googleImgLayer',
    source: new XYZ({
        url: 'http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}',
        crossOrigin: "anonymous"
    })
});
const googleVecLayer = new TileLayer({//谷歌街道
    visible: false,
    key: 'googleVecLayer',
    source: new XYZ({
        url: 'http://www.google.cn/maps/vt?lyrs=m@189&gl=cn&x={x}&y={y}&z={z}',
        crossOrigin: "anonymous"
    })
});
const gaodeImgLayer = new TileLayer({//高德影像
    visible: false,
    key: 'gaodeImgLayer',
    source: new XYZ({
        url: 'http://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
        crossOrigin: "anonymous"
    })
});
const gaodeVecLayer = new TileLayer({//高德街道
    visible: false,
    key: 'gaodeVecLayer',
    source: new XYZ({
        url: 'http://webrd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}',
        crossOrigin: "anonymous"
    })
});

crossOrigin: "anonymous":

这个属性用于设置跨源资源共享(CORS)的请求策略。当您需要从不同的域名加载资源(如瓦片地图)时,浏览器的同源策略会阻止您读取从其他域加载的资源的内容,除非响应头中有适当的CORS头信息。

"anonymous"的意思是,将会对这个资源的请求不会包含用户凭证(如cookies或者HTTP认证信息)。如果服务器响应包含Access-Control-Allow-Origin的头信息,并且允许来自原始请求的域,那么客户端(浏览器)就可以读取加载的资源。如果不设置crossOrigin属性,那么在尝试将地图瓦片绘制到canvas上时,由于安全限制,您将无法执行涉及像素操作的任务,比如导出地图为图片。

visible: false:

这个属性用来设置图层的初始可见性。当设为false时,这个图层在地图上将默认不可见。这个属性可以在地图运行时动态改变,以此控制图层的显示与隐藏。例如,你可能有多个图层供用户选择,但你不想一开始就将它们全部显示出来。

用visible把bing地图设置为默认地图

下一步把这些图层放到一个图层数组中

// 图层组
const layersData = [
    bingMapLayer,
    satelliteLayer,
    streetLayer,
    terLayer,
    satelliteLabelLayer,
    streetLabelLayer,
    osmLayer,
    arcgisImgLayer,
    arcgisVecLayer,
    googleImgLayer,
    googleVecLayer,
    gaodeImgLayer,
    gaodeVecLayer,
];

然后再定义一个数组mapconfig用来储存定义展示工具栏

const mapconfig = [
    {"layer_id": "bingMapLayer", "label": "必应卫星", "className": "imgType"},//Bing卫星图
    {"layer_id": "satelliteLayer", "label": "天地卫星", "className": "imgType"},//天地图卫星图
    {"layer_id": "streetLayer", "label": "天地街道", "className": "vecType"},//天地图街道图
    {"layer_id": "satelliteLabelLayer", "label": "街道标注", "className": "vecType"},//天地图街道标注
    {"layer_id": "streetLabelLayer", "label": "天地行政", "className": "vecType"},//天地图行政图
    {"layer_id": "terLayer", "label": "天地地形", "className": "terType"},//天地图地形图
    {"layer_id": "osmLayer", "label": "OSM", "className": "vecType"},//OSM地图
    {"layer_id": "arcgisImgLayer", "label": "esri影像", "className": "imgType"},//arcgis影像
    {"layer_id": "arcgisVecLayer", "label": "esri街道", "className": "vecType"},//arcgis街道
    {"layer_id": "googleImgLayer", "label": "谷歌影像", "className": "imgType"},//谷歌影像
    {"layer_id": "googleVecLayer", "label": "谷歌街道", "className": "vecType"},//谷歌街道
    {"layer_id": "gaodeImgLayer", "label": "高德影像", "className": "imgType"},//高德影像
    {"layer_id": "gaodeVecLayer", "label": "高德街道", "className": "vecType"},//高德街道
];

最后把layersData和mapconfig暴露出去,方便调用

export {layersData, mapconfig}

回到baseMap中

这时把layersData和mapconfig引入进来,然后把layersData放到Map实例里面的layers中

完整代码如下:

<template>
  <div>
    <div id="map" ref="mapContainer"></div>
  </div>
</template>

<script>
import {onMounted, ref} from 'vue';
import "ol/ol.css";
import Map from 'ol/Map';
import View from 'ol/View';
import {layersData,mapconfig} from './mapLayers'

export default {
  name: "baseMap",
  setup() {
    const mapContainer = ref(null); // 使用 ref 创建响应式引用
    const mapInstance = ref(null);  // 存储地图实例的引用
    // 初始化地图的方法
    const initMap = () => {

      // 创建地图实例
      mapInstance.value = new Map({
        target: mapContainer.value, // 使用 ref 引用的 DOM 元素作为目标
        layers: [...layersData],
        view: new View({
          center: [0, 0], // 根据需要设置中心
          zoom: 2,        // 根据需要设置缩放级别
        }),
      });
    };
    // 组件挂载后初始化地图
    onMounted(initMap);
    // 返回在模板中需要的变量
    return {
      mapContainer,
      mapInstance
    };
  }
};
</script>
<style scoped>
#map {
  width: 100%;
  height: 100vh;
}
</style>

这是如果你没写错的话,地图毫无变化

接下来就是构建切换工具栏了

创建一个loadSwitcherMap.js文件

然后定义一个构造函数,用于创建一个基础图层切换工具栏的实例

代码如下:

// 定义一个构造函数,用于创建一个基础图层切换工具栏的实例
const BaseLayerSwitcherToolBar = function (options) {
    // console.log('options', options)
    // 如果传入了options对象,则使用该对象,否则创建一个空对象
    var opt_options = options ? options : {};
    // selectedIndex用于记录当前选中的工具项的索引,默认为0或者由用户指定
    this.selectedIndex = opt_options.selectedIndex ? opt_options.selectedIndex : 0;

    // 如果传入了target选项,则尝试获取对应的DOM元素或使用传入的DOM元素
    var target = this.target = opt_options.target;
    if (this.target) {
        // 如果target是字符串,则假设它是DOM元素的ID,并尝试获取该元素
        if (typeof target === 'string') {
            this.target = document.getElementById(target);
        }
    } else {
        // 如果未指定target,则创建一个新的div作为容器,并设置其类名
        this.target = document.createElement("div");
        this.target.className = "map_switch";
    }

    // 绑定事件处理函数:鼠标悬停和移出
    var _this = this;
    target = this.target;
    target.onmouseover = function () {
        _this._handleItemMouseover(this);
    };
    target.onmouseout = function () {
        _this._handleItemMouseout(this);
    };

    // 初始化elements数组,用于存储所有工具项元素
    this.elements = [];
    // onItemClick事件处理器,当工具项被点击时触发
    this.onItemClick = null;

    // 如果提供了data选项,则使用setData方法设置工具栏的数据
    var data = opt_options.data;
    if (data) {
        this.setData(opt_options.data);
    }
}

// setData方法用于初始化工具栏的工具项
BaseLayerSwitcherToolBar.prototype.setData = function (data) {
    var _this = this;
    var ctlDiv = this.target;

    var element, item;
    for (var i = 0, length = data.length; i < length; i++) {
        item = data[i];
        // 使用_createElementByItem方法为每项数据创建一个DOM元素
        element = _this._createElementByItem(item);
        // 设置该元素的item_index属性,用于后续识别点击的是哪个工具项
        element.setAttribute("item_index", i);
        // 将创建的元素添加到elements数组中
        _this.elements.push(element);
        // 绑定工具项的点击事件处理器
        element.onclick = function () {
            // 更新selectedIndex为当前点击的工具项索引
            var index = _this.selectedIndex = this.getAttribute("item_index");
            // 调用_updateState方法更新工具项的可见状态
            _this._updateState();
            // 如果有设置onItemClick事件处理器,则调用该处理器
            if (_this.onItemClick) {
                _this.onItemClick(data[index], index, this);
            }
        }
        // 根据是否为当前选中的工具项来设置其显示或隐藏
        element.style.display = i == _this.selectedIndex ? "block" : "none";
        // 将工具项的DOM元素添加到目标容器中
        ctlDiv.appendChild(element);
    }
}

// _createElementByItem方法用于根据提供的数据创建工具项的DOM元素
BaseLayerSwitcherToolBar.prototype._createElementByItem = function (item) {
    // 提取item的标签和类名
    var label = item.label;
    var className = item.className;

    // 创建工具项的最外层div,并设置其类名
    var itemDiv = document.createElement("div");
    itemDiv.className = "map_switch_item";

    // 创建表示悬停状态的div,并设置其类名
    var itemHover = document.createElement("div");
    itemHover.className = "hoverType";
    itemDiv.appendChild(itemHover);

    // 创建表示图层类型的div,并设置其类名为提供的className
    var itemType = document.createElement("div");
    itemType.className = "vecType";
    itemType.className = className; // 这里可能有误,应该是添加一个新类名,而不是替换
    itemHover.appendChild(itemType);

    // 创建用于显示标签文字的div,并设置其类名
    var itemLabel = document.createElement("div");
    itemLabel.className = "map_bom";
    // 如果提供了标签,则设置该div的文本
    if (label) {
        itemLabel.innerText = label;
    }
    itemHover.appendChild(itemLabel);

    // 返回创建的工具项DOM元素
    return itemDiv;
}

// _updateState方法用于更新工具项的显示状态
BaseLayerSwitcherToolBar.prototype._updateState = function () {
    var div;
    for (var i = 0, length = this.elements.length; i < length; i++) {
        div = this.elements[i];
        // 除了选中的工具项之外,其他工具项都隐藏
        div.style.display = i != this.selectedIndex ? "none" : "block";
    }
}

// _handleItemMouseover方法处理鼠标悬停在工具栏上的动作
BaseLayerSwitcherToolBar.prototype._handleItemMouseover = function (elem) {
    // 设置鼠标悬停时工具栏的宽度为auto
    elem.style.width = "auto";
    // 显示所有工具项
    for (var i = 0, length = this.elements.length; i < length; i++) {
        this.elements[i].style.display = "block";
    }
}

// _handleItemMouseout方法处理鼠标移出工具栏的动作
BaseLayerSwitcherToolBar.prototype._handleItemMouseout = function (elem) {
    // 设置鼠标移出时工具栏的宽度为70px
    elem.style.width = "70px";
    var div;
    for (var i = 0, length = this.elements.length; i < length; i++) {
        div = this.elements[i];
        // 除了选中的工具项,其他工具项都隐藏
        div.style.display = i == this.selectedIndex ? "block" : "none";
    }
}

// 使用export语句导出BaseLayerSwitcherToolBar,使其可以在其他模块中通过import引入
export {BaseLayerSwitcherToolBar}

写完代码不要忘记再baseMap中引用BaseLayerSwitcherToolBar

创建工具栏

const addControls = () => {
      loadSwitcherMap(mapconfig); // 底图切换控件加载
    };

    const loadSwitcherMap = (data) => {
      const baseLayerSwitcherToolbar = new BaseLayerSwitcherToolBar({
        data
      });
      document.getElementById("map").appendChild(baseLayerSwitcherToolbar.target);
      baseLayerSwitcherToolbar.onItemClick = function (itemData) {
        let data = itemData;
        layersData.forEach(function (layer) {
          data.layer_id === layer.get('key') ? layer.setVisible(true) : layer.setVisible(false);
        });
        console.log(layersData);
      };
    };

工具栏CSS样式

/*底图切换控件*/
.map_switch {
  position: absolute;
  right: 50px;
  top: 15px;
  font-family: "宋体";
  font-size: 12px;
  width: 70px;
  z-index: 999;
}

.map_switch_item {
  width: 58px;
  height: 60px;
  border: 1px solid #b1b1b1;
  text-align: center;
  float: right;
  display: inline;
  margin-left: 10px;
  background-color: #fff;
  cursor: pointer;
}

.hoverType:hover {
  width: 56px;
  height: 58px;
  background-color: #c6e0f7;
  margin: auto;
}

.hoverType {
  width: 56px;
  height: 58px;
  background-color: #fff;
  margin: auto;
}

.vecType {
  width: 56px;
  height: 38px;
  background: url(../assets/indexImage.png) no-repeat -154px -202px;
}

.imgType {
  width: 56px;
  height: 38px;
  background: url(../assets/indexImage.png) no-repeat -154px -155px;
}

.terType {
  width: 56px;
  height: 38px;
  background: url(../assets/indexImage.png) no-repeat -86px -202px;
}

.map_bom {
  height: 22px;
  line-height: 22px;
  color: #333333;
}

源代码我会放到getee,git clone https://gitee.com/lifangxing1314/multiple-map-switchingvue3.git

  • 33
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Vue3是最新版本的Vue.js框架,是一种现代的JavaScript框架,用于构建交互式和响应式的应用程序。OpenLayers是一个很受欢迎的用于Web地图应用程序的JavaScript库。 Vue3和OpenLayers两者结合使用可以创建出高级的Web地图应用程序,因为Vue3拥有丰富的生态系统和丰富的组件库,能够轻松地集成OpenLayers的功能和样式。Vue3非常适合用于开发Web应用程序,因为它提供了许多工具和功能来优化应用程序的性能和开发体验。 通过Vue3,您可以轻松地将OpenLayers与其他Vue组件集成在一起,以创建具有高级功能和交互性的地图应用程序。Vue3的响应式数据绑定特性可以轻松地更新OpenLayers图层和标记等地图元素。Vue3还提供了一种方便的方法来管理地图事件,您可以方便地为每个地图事件创建自定义处理程序和生命周期钩子。 总之,Vue3和OpenLayers的结合使用可以为您来一个强大的Web地图应用程序框架,提高开发和设计的效率,并在可扩展性和性能方面提供更好的解决方案。 ### 回答2: Vue3是一种现代web应用程序框架,可以用于构建基于数据驱动的单页面应用程序。OpenLayers则是一种开源JavaScript库,用于在Web上呈现交互式地图和地理空间数据。 Vue3和OpenLayers的结合可以为开发人员提供一种强大的工具,用于构建可视化地图和地理空间数据的应用程序。Vue3可以通过其组件系统便于地组织代码和数据,并且可以使用其响应式数据绑定来更新UI。OpenLayers可以允许开发人员轻松地创建交互式地图,并使用其丰富的API来操作地图的样式和数据。 与以前版本的Vue相比,Vue3的主要优势是其重新设计的响应式API。Vue3的响应式API更加高效和灵活,使得开发人员可以更轻松地处理大量数据的更改。在开发地图应用程序时,这非常重要,因为大量的地理空间数据会随时间而变化。Vue3的新特性还包括更简单的编译器、更好的渲染性能和更好的TypeScript支持。 熟练使用Vue3和OpenLayers可以使开发人员轻松构建功能强大且可定制的地图应用程序。使用组件化的方法来管理应用程序代码,可以使应用程序更容易维护和扩展。OpenLayers强大的功能可以使开发人员制定定制的地图风格和数据显示,从而为用户提供更丰富的交互体验。总而言之,Vue3和OpenLayers的结合是开发地图应用程序的绝佳选择。 ### 回答3: Vue3-OpenLayers是基于Vue3框架的客户端Web地图库。该库结合了Vue3的响应式数据流和OpenLayers的强大地图功能,提供了一个简单易用、灵活可定制的地图开发框架。通过Vue3-OpenLayers,开发者可以较为轻松地搭建起自己的地图应用,实现各种地图需求。 Vue3-OpenLayers库的主要特点包括: 1. 基于Vue3响应式系统,便于数据更新和管理; 2. 提供了完整的OpenLayers组件库,如地图、图层、控件、交互等,方便使用; 3. 支持灵活的地图样式配置和自定义功能开发; 4. 支持OpenLayers的各种地图源,如瓦片图、WMS、WMTS等等。 Vue3-OpenLayers库的使用对于有Vue3开发经验的开发者而言较为友好,借助Vue3的响应式能力,实现地图数据和业务逻辑的跟随变化响应,并且组件化的设计模式方便了多层级嵌套的地图应用开发。同时,开发者还可以利用Vue3提供的组件化构建特性,设计出符合自己需求的定制化地图组件。 总之,Vue3-OpenLayers是一款强大的地图库,它将Vue3和OpenLayers优势结合起来,为地图应用开发者提供了更为简单、灵活的地图开发解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值