vue openlayers——图层控制 切换底图

openlayers官网:

OpenLayers - Welcomehttps://openlayers.org/

1、创建地图

获取页面节点放置地图的节点元素,设置地图中心点,创建地图

const mapContainer = shallowRef<HTMLDivElement | null>(null);
const map = shallowRef<olMapType | null>(null);
const center = [115.428784, 23.088291];

const initMap = () => {
  const target = mapContainer.value;
  const _map = createMap(target!, center); //创建地图
  map.value = _map;
  // 当鼠标移入到marker上时 鼠标显示手的样式
  _map.on("pointermove", (e) => {
    const pixel = _map.getEventPixel(e.originalEvent);
    const hit = _map.hasFeatureAtPixel(pixel);
    (_map.getTarget() as HTMLElement).style.cursor = hit ? "pointer" : "";
  });
  _map.addControl(
    new ScaleLine({
      units: "metric",
      className: "ol-scale-line",
    })
  );
};

2、地图图标以及坐标数据(坐标点数据可以通过百度地图坐标拾取器获取)

//图层1
const imgSrc1 =
  "https://img1.baidu.com/it/u=2973931732,461284785&fm=253&fmt=auto&app=138&f=PNG?w=200&h=200";

//坐标点
const infoList1 = [
  {
    lng: "105.428784",
    lat: "23.088291",
  },
  {
    lng: "135.428784",
    lat: "24.088291",
  },
];

//图层2
const imgSrc2 =
  "https://img0.baidu.com/it/u=1011815455,1718073395&fm=253&fmt=auto&app=138&f=PNG?w=256&h=256";
const infoList2 = [
  { lng: "107.927905", lat: "33.378945" },
  { lng: "128.227905", lat: "44.478945" },
  { lng: "139.327905", lat: " 34.578945" },
  { lng: "124.227905", lat: "35.578945" },
];

const imgSrc3 =
  "https://img2.baidu.com/it/u=552603751,2471267685&fm=253&fmt=auto&app=120&f=PNG?w=220&h=150";
const infoList3 = [
  { lng: "101.927905", lat: "32.378945" },
  { lng: "92.227905", lat: "22.478945" },
  { lng: "113.327905", lat: "23.578945" },
  { lng: "72.227905", lat: "25.578945" },
];

3、添加地图标记markers图层

//添加地图标记
const showMarkets = (
  infoList: any,
  imgSrc: string,
  name: string,
  scale: number
) => {
  //判断地图是否有该图层,图层控制,移出之后再添加时避免重复添加使用的
  const addLayers = getLayerInfo(name);
  if (map.value && !addLayers) {
    addMarkers(map.value, infoList, imgSrc, { name: name }, { scale: scale });
  }
};

const addMarkersLayers = () => {
  showMarkets(infoList1, imgSrc1, "infoList1", 0.2); //蓝色
  showMarkets(infoList2, imgSrc2, "infoList2", 0.2); //红色
  showMarkets(infoList3, imgSrc3, "infoList3", 0.4); //绿色
};

4、判断地图图层是否包含某一个图层的方法

const getLayerInfo = (name: string) => {
  const layers = map.value?.getAllLayers();
  if (layers) {
    return layers.find((item: any) => item?.getProperties().name == name);
  }
};

5、地图图标图层控制,勾选显示,取消则移除图层

首先要判断地图是否已经存在了该图层,存在时勾选不添加,不存在时则添加图层;取消勾选移除图层

const checkList = ref(["红色图层", "蓝色图层", "绿色图层"]);
const checkboxGroup1 = ref(["红色图层", "蓝色图层", "绿色图层"]);

//该部分可以优化,这里不是简写
const handleChange = function (value: any) {
  const redLayer = getLayerInfo("infoList2");
  if (value.includes("红色图层")) {
    if (redLayer) {
      //已存在
    } else {
      //添加
      showMarkets(infoList2, imgSrc2, "infoList2", 0.2);
    }
  } else {
    //@ts-ignore
    map.value?.removeLayer(redLayer);
  }

  const blueLayer = getLayerInfo("infoList1");
  if (value.includes("蓝色图层")) {
    if (blueLayer) {
      //已存在
    } else {
      //添加
      showMarkets(infoList1, imgSrc1, "infoList1", 0.2);
    }
  } else {
    //@ts-ignore
    map.value?.removeLayer(blueLayer);
  }

  const greenLayer = getLayerInfo("infoList3");
  if (value.includes("绿色图层")) {
    if (greenLayer) {
      //已存在
    } else {
      //添加
      showMarkets(infoList3, imgSrc3, "infoList3", 0.4);
    }
  } else {
    //@ts-ignore
    map.value?.removeLayer(greenLayer);
  }
};

6、切换地图底图

const changeLayer = (type: "street" | "satellite" | "dark") => {
  let layerUrl = "";
  switch (type) {
    case "street":
      layerUrl = streetUrl;
      break;
    case "satellite":
      layerUrl = satelliteUrl;
      break;
    case "dark":
      layerUrl = darkUrl;
      break;
    default:
      return;
  }
  if (map.value) setLayer(layerUrl, map.value as olMapType);
};

7、页面效果图

取消勾选

切换底图 

 8、完整代码

map.vue

<template>
  <button @click="changeLayer('dark')">切换暗色底图</button>
  <button @click="changeLayer('street')">切换街道底图</button>
  <button @click="changeLayer('satellite')">切换卫星底图</button>
  <div ref="mapContainer" class="mapContainer">
    <div class="content">
      <el-checkbox-group
        v-model="checkboxGroup1"
        size="large"
        @change="handleChange"
      >
        <el-checkbox
          v-for="(item, index) in checkList"
          :key="index"
          :label="item"
          border
        />
      </el-checkbox-group>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { onMounted, shallowRef } from "vue";
import { createMap, setLayer, addMarkers } from "./map";
import type { Map as olMapType } from "ol";
import { ScaleLine } from "ol/control";

import { streetUrl, satelliteUrl, darkUrl } from "@/config";
onMounted(function () {
  initMap();
  addMarkersLayers();
});

const mapContainer = shallowRef<HTMLDivElement | null>(null);
const map = shallowRef<olMapType | null>(null);
const center = [115.428784, 23.088291];

const initMap = () => {
  const target = mapContainer.value;
  const _map = createMap(target!, center); //创建地图
  map.value = _map;
  // 当鼠标移入到marker上时 鼠标显示手的样式
  _map.on("pointermove", (e) => {
    const pixel = _map.getEventPixel(e.originalEvent);
    const hit = _map.hasFeatureAtPixel(pixel);
    (_map.getTarget() as HTMLElement).style.cursor = hit ? "pointer" : "";
  });
  _map.addControl(
    new ScaleLine({
      units: "metric",
      className: "ol-scale-line",
    })
  );
};

//图层1
const imgSrc1 =
  "https://img1.baidu.com/it/u=2973931732,461284785&fm=253&fmt=auto&app=138&f=PNG?w=200&h=200";

//坐标点
const infoList1 = [
  {
    lng: "105.428784",
    lat: "23.088291",
  },
  {
    lng: "135.428784",
    lat: "24.088291",
  },
];

//图层2
const imgSrc2 =
  "https://img0.baidu.com/it/u=1011815455,1718073395&fm=253&fmt=auto&app=138&f=PNG?w=256&h=256";
const infoList2 = [
  { lng: "107.927905", lat: "33.378945" },
  { lng: "128.227905", lat: "44.478945" },
  { lng: "139.327905", lat: " 34.578945" },
  { lng: "124.227905", lat: "35.578945" },
];

const imgSrc3 =
  "https://img2.baidu.com/it/u=552603751,2471267685&fm=253&fmt=auto&app=120&f=PNG?w=220&h=150";
const infoList3 = [
  { lng: "101.927905", lat: "32.378945" },
  { lng: "92.227905", lat: "22.478945" },
  { lng: "113.327905", lat: "23.578945" },
  { lng: "72.227905", lat: "25.578945" },
];
//添加地图标记
const showMarkets = (
  infoList: any,
  imgSrc: string,
  name: string,
  scale: number
) => {
  //判断地图是否有该图层,图层控制,移出之后再添加时避免重复添加使用的
  const addLayers = getLayerInfo(name);
  if (map.value && !addLayers) {
    addMarkers(map.value, infoList, imgSrc, { name: name }, { scale: scale });
  }
};

const addMarkersLayers = () => {
  showMarkets(infoList1, imgSrc1, "infoList1", 0.2); //蓝色
  showMarkets(infoList2, imgSrc2, "infoList2", 0.2); //红色
  showMarkets(infoList3, imgSrc3, "infoList3", 0.4); //绿色
};

const getLayerInfo = (name: string) => {
  const layers = map.value?.getAllLayers();
  if (layers) {
    return layers.find((item: any) => item?.getProperties().name == name);
  }
};

const checkList = ref(["红色图层", "蓝色图层", "绿色图层"]);
const checkboxGroup1 = ref(["红色图层", "蓝色图层", "绿色图层"]);

//该部分可以优化,这里不是简写
const handleChange = function (value: any) {
  const redLayer = getLayerInfo("infoList2");
  if (value.includes("红色图层")) {
    if (redLayer) {
      //已存在
    } else {
      //添加
      showMarkets(infoList2, imgSrc2, "infoList2", 0.2);
    }
  } else {
    //@ts-ignore
    map.value?.removeLayer(redLayer);
  }

  const blueLayer = getLayerInfo("infoList1");
  if (value.includes("蓝色图层")) {
    if (blueLayer) {
      //已存在
    } else {
      //添加
      showMarkets(infoList1, imgSrc1, "infoList1", 0.2);
    }
  } else {
    //@ts-ignore
    map.value?.removeLayer(blueLayer);
  }

  const greenLayer = getLayerInfo("infoList3");
  if (value.includes("绿色图层")) {
    if (greenLayer) {
      //已存在
    } else {
      //添加
      showMarkets(infoList3, imgSrc3, "infoList3", 0.4);
    }
  } else {
    //@ts-ignore
    map.value?.removeLayer(greenLayer);
  }
};

const changeLayer = (type: "street" | "satellite" | "dark") => {
  let layerUrl = "";
  switch (type) {
    case "street":
      layerUrl = streetUrl;
      break;
    case "satellite":
      layerUrl = satelliteUrl;
      break;
    case "dark":
      layerUrl = darkUrl;
      break;
    default:
      return;
  }
  if (map.value) setLayer(layerUrl, map.value as olMapType);
};
</script>
<style lang="scss" scoped>
.content {
  background-color: white;
  border-radius: 0.25rem;
  padding: 1rem;
  position: absolute;
  left: 1.5rem;
  top: 1rem;
  z-index: 9999;
  width: 150px;
  min-width: 150px;
  .el-checkbox {
    width: 100%;
    margin-bottom: 10px;
  }
}

.mapContainer {
  width: 100%;
  height: 800px;

  position: relative;
}
</style>

map.ts

import { Feature, Map, View } from "ol";
import { defaults as defaultControls } from "ol/control";
import { Point } from "ol/geom";
import TileLayer from "ol/layer/Tile";
import VectorLayer from "ol/layer/Vector";
import { Cluster, Vector as VectorSource, XYZ } from "ol/source";
import OSM from "ol/source/OSM";
import { Icon, Style } from "ol/style";
import { MapOptions } from "ol/PluggableMap";
import { satelliteUrl } from "@/config";

/**
 * @description 创建一个地图实例
 * @param {Document | DocumentId} target 地图挂载到的容器
 * @param {Array} center 地图中心点
 * @param {Object} config 其他配置项
 * @returns 地图对象
 */
export const createMap = function (
  target: HTMLElement | string,
  center: number[],
  config: MapOptions = {}
): Map {
  const view = new View({
    projection: "EPSG:4326",
    center: center, // 需要
    zoom: 14,
    maxZoom: 18,
    // minZoom: 16,
    constrainResolution: true, //自动缩放到距离最近的一个整数级别,因为当缩放在非整数级别时地图会糊
  });
  // 创建地图对象
  const map = new Map({
    target,
    layers: [
      new TileLayer({
        source: new OSM(),
      }),
    ],
    controls: defaultControls({
      zoom: false, //不显示放大放小按钮;
      rotate: false, //不显示指北针控件;
      attribution: false, //不显示右下角的地图信息控件;
    }).extend([]),
    view,
    ...config,
  });
  setLayer(satelliteUrl, map);
  return map;
};
/**
 * @description 设置地图的地图
 * @param {String} layerUrl 地图地址
 * @param {Objcet} map 当前的地图实例
 */
export const setLayer = function (layerUrl: string, map: Map) {
  const satelliteE = new XYZ({
    crossOrigin: "anonymous",
    url: layerUrl,
  });
  const baseLayer2 = map.getLayers().item(0);
  // FIXME
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  baseLayer2.setSource(satelliteE);
};
/**
 * @description 循环创建marker
 * @param {Object} map 绑定的地图对象
 * @param {Array} el 含有经纬度的数组
 */
export function addMarkers(
  map: Map,
  lnglats: any,
  imgSrc: string,
  options = {},
  styleConfig = {}
) {
  // 创建Feature对象集合
  const features1 = [];
  for (let i = 0; i < lnglats.length; i++) {
    const numAry = new Feature({
      geometry: new Point([Number(lnglats[i].lng), Number(lnglats[i].lat)]),
      ...lnglats[i],
    });
    features1.push(numAry);
  }
  // 矢量要素数据源
  const source1 = new VectorSource({
    features: features1,
  });
  const clusterSource = new Cluster({
    distance: 5, //标注之间
    source: source1,
  });

  const iconStyle = markerStyle(imgSrc, styleConfig);

  const clusters = new VectorLayer({
    source: clusterSource,
    style: iconStyle,
    zIndex: 999,
    ...options,
  });
  map.addLayer(clusters);
  return source1;
}

const markerStyle = (imgSrc: any, styleConfig: {}) => {
  const img = imgSrc ? imgSrc : ``;
  return new Style({
    image: new Icon({
      opacity: 1,
      src: img,
      anchor: [0.5, 3], // 偏移位置
      anchorOrigin: "bottom-left",
      anchorXUnits: "fraction",
      anchorYUnits: "pixels",
      offsetOrigin: "top-right",
      offset: [0, 1], //偏移量设置
      scale: 1, //图标缩放比例
      ...styleConfig,
    }),
  });
};

config/index.ts

// 街景地图地址
export const streetUrl =
  "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}";
// 卫星图地址
export const satelliteUrl =
  "http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=6";

//暗色地图
export const darkUrl =
  "http://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}";

<think>好的,我现在需要在Vue3项目中使用OpenLayers实现地图图层的动态切换。根据用户提供的引用内容,我应该先回顾一下相关的参考资料,然后结合这些资料来构建解决方案。 首先,引用[1]提到了在Vue3中使用elementplus制作图层控件,包括图层切换、显示隐藏和排序。这说明可能需要结合Element UI的组件,比如按钮或下拉菜单来控制图层的状态。同时,引用[2]指出OpenLayers支持多种地图来源,并且可以叠加图层。这提示我需要创建多个图层,并管理它们的显示和隐藏。 接下来,引用[4]展示了如何将多个图层添加到组内,例如亚洲图层和中国图层,并通过组来管理。这可能是关键步骤,因为图层组可以方便地控制一组图层的可见性。例如,创建不同的图层组,然后通过切换组的可见性来实现动态切换。 然后,引用[3]讨论了右键菜单功能的实现,其中可能涉及事件处理和图层操作。虽然动态切换图层可能不需要右键菜单,但事件处理的经验可能有帮助,比如在点击按钮时触发图层切换的函数。 步骤可能包括: 1. 安装必要的库,如OpenLayers和可能的UI组件库(element-plus)。 2. 创建多个图层实例,可能是不同的地图来源或自定义图层。 3.图层组织成图层组,便于管理。 4.Vue组件中创建控制UI,如按钮或开关,绑定到图层的可见性属性。 5. 编写方法,当用户操作UI时,切换相应图层的可见性。 需要注意的地方: - OpenLayers图层实例需要正确初始化,并添加到地图对象中。 - 动态切换可能需要使用Vue的响应式数据来同步图层状态。 - 图层组的应用可能简化多个图层的管理,但需要确认是否每个图层都需要单独控制还是分组控制更合适。 可能的问题: - 如何确保图层切换时不会重复添加或移除? - 如何处理图层的顺序(zIndex)以确保正确的显示层级? - 如何优化性能,特别是当图层数量较多时? 需要查阅OpenLayers的文档,确认图层组的使用方法以及如何设置图层的可见性。此外,参考示例中的代码结构,比如如何创建图层组并添加到地图中,然后将图层的visible属性与UI控件绑定。 综合以上思考,步骤应分为环境搭建、图层创建、图层组管理、UI控件绑定和切换逻辑实现几个部分。确保每一步都正确无误,并进行测试验证效果。</think>在Vue3中通过OpenLayers实现地图图层的动态切换,可以按照以下步骤操作: --- ### 一、环境搭建与依赖安装 1. 安装核心库: ```bash npm install ol vue3-openlayers element-plus ``` 2. 引入Element Plus组件库用于构建控制UI[^1] --- ### 二、创建图层图层组 ```typescript // 在Vue组件中定义图层 import { Tile as TileLayer, Group as LayerGroup } from 'ol/layer'; import OSM from 'ol/source/OSM'; import XYZ from 'ol/source/XYZ'; // 基础底图 const osmLayer = new TileLayer({ source: new OSM(), visible: true, title: 'OSM地图' }); // 卫星图 const satelliteLayer = new TileLayer({ source: new XYZ({ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}' }), visible: false, title: '卫星图' }); // 创建图层组 const layerGroup = new LayerGroup({ layers: [osmLayer, satelliteLayer] }); ``` --- ### 三、地图初始化 ```html <template> <ol-map ref="map" :layers="layerGroup"> <ol-view :center="[116.4, 39.9]" :zoom="5" /> </ol-map> </template> <script setup> import { ref } from 'vue'; const map = ref(null); </script> ``` --- ### 四、实现切换控制逻辑 1. **添加UI控件**(使用Element Plus): ```html <el-radio-group v-model="activeLayer"> <el-radio-button label="osm">OSM地图</el-radio-button> <el-radio-button label="satellite">卫星图</el-radio-button> </el-radio-group> ``` 2. **响应式数据绑定**: ```typescript const activeLayer = ref('osm'); watch(activeLayer, (newVal) => { layerGroup.getLayers().forEach(layer => { const title = layer.get('title'); layer.setVisible( (newVal === 'osm' && title === 'OSM地图') || (newVal === 'satellite' && title === '卫星图') ); }); }); ``` --- ### 五、关键原理说明 1. **图层可见性控制**:通过`layer.setVisible()`方法动态修改可见性[^4] 2. **图层组管理**:使用`LayerGroup`统一管理多个相关图层 3. **响应式更新**:Vue3的watch监听器实现UI状态与图层状态同步 --- ### 六、扩展优化建议 1. **添加动画过渡**:在切换时使用`layer.setOpacity()`实现淡入淡出效果 2. **图层排序控制**:通过`layer.setZIndex()`调整显示层级 3. **动态加载图层**:使用WMS或GeoJSON源实现按需加载 ```typescript // 示例:动态添加行政区划图层 const addBoundaryLayer = () => { const vectorLayer = new VectorLayer({ source: new VectorSource({ url: '/data/boundary.geojson', format: new GeoJSON() }) }); layerGroup.getLayers().push(vectorLayer); }; ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值