当前位置: 首页 > article >正文

Vue2+OpenLayers给标点Feature添加信息窗体(提供Gitee源码)

目录

一、案例截图

二、安装OpenLayers库

三、代码实现

3.1、信息窗体DOM元素

3.2、创建Overlay

3.3、创建一个点

3.4、给点初始化点击事件

3.5、完整代码

四、Gitee源码


一、案例截图

二、安装OpenLayers库

npm install ol

三、代码实现

初始化变量:

  data() {
    return {
      map:null,
      overLay:null,
      //所有点信息都放在这个图层
      pointLayer: new VectorLayer({
        source: new VectorSource(),
      }),
    }
  },

3.1、信息窗体DOM元素

关键代码:

  <div>
    <div id="map-container"></div>
    <div id="popup-box" class="popup-box">
      <button id="close-button" class="close-button">&times;</button>
      <div id="popup-content" class="popup-content"></div>
    </div>
  </div>

css样式:

<style scoped>

#map-container {
  width: 100%;
  height: 100vh;
}
.popup-box {
  background: rgba(255, 255, 255, 0.95);
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 20px;
  z-index: 1000;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
  transition: all 0.3s ease;
  max-width: 300px;
  font-family: 'Arial', sans-serif;
  position: absolute;
  transform: translate(-50%, -100%); /* 使弹出框上移并居中 */
}

/* 添加箭头样式 */
.popup-box::after {
  content: "";
  position: absolute;
  top: 100%; /* 箭头位于弹出框的底部 */
  left: 50%; /* 箭头横向居中 */
  margin-left: -6px; /* 调整箭头与弹出框的间距 */
  border-width: 6px; /* 箭头的大小 */
  border-style: solid;
  border-color: rgba(255, 255, 255, 0.95) transparent transparent transparent; /* 箭头的颜色 */
}

.close-button {
  background: none;
  color: gray;
  border: none;
  font-size: 20px;
  position: absolute;
  top: 10px;
  right: 10px;
  cursor: pointer;
}

.popup-content {
  width: 240px;
  margin-top: 10px;
  font-size: 16px;
  line-height: 1.5;
}
</style>

3.2、创建Overlay

覆盖物(Overlay)是用于在地图上显示额外的HTML元素,如弹出窗口、信息框、控件等的层。与图层不同,覆盖物不直接渲染地理要素,而是用于展示与地图位置相关的HTML内容。

element (必需):指定用于作为Overlay内容的DOM元素。通常是一个HTML元素,例如div,作为弹出框或工具提示等。

autoPan (可选) :boolean 或 { animation: { duration: number } } 默认值:false,如果设置为 true,地图将在Overlay显示时自动平移,以确保Overlay的位置始终在视图范围内。如果设置为对象,可以自定义平移时的动画效果。例如,设置动画持续时间。

position (可选) :[number, number](坐标数组) 默认值:undefined,初始化时设置Overlay的位置,指定为地图坐标系中的坐标。如果未指定,Overlay不会显示。

stopEvent (可选) :默认值:true,确定Overlay的点击事件是否会停止事件传播。如果设置为 false,点击 Overlay 的内容不会阻止点击事件向下传播到地图层。

offset (可选) :默认值:[0, 0] 说明:Overlay内容相对于指定位置的偏移量,用于微调Overlay的显示位置。例如,可以通过提供负值来向上或向左移动Overlay。

关键代码:

let popupBox = document.getElementById('popup-box');
let closeButton = document.getElementById('close-button');
//用于显示详情页面的窗口(根据经纬度来的,位置不固定)
this.overlay = new Overlay({
  element: popupBox,
  autoPan: {
    animation: {
      duration: 250,
    },
  },
  offset:[0,-20],
});
this.map.addOverlay(this.overlay);
// 关闭弹出框的事件处理
let _that = this;
closeButton.addEventListener('click', () => {
  _that.overlay.setPosition(undefined); // 关闭弹出框
});

3.3、创建一个点

关键代码:

/**
 * 根据经纬度坐标添加自定义图标 支持base64
 */
addPoints(coordinate) {
  // 创建feature要素,一个feature就是一个点坐标信息
  let feature = new Feature({
    geometry: new Point(coordinate),
  });
  // 设置要素的图标
  feature.setStyle(
      new Style({
        // 设置图片效果
        image: new Icon({
          src: 'http://api.tianditu.gov.cn/img/map/markerA.png',
          // anchor: [0.5, 0.5],
          scale: 1,
        }),
      }),
  );
  return feature;
},

将其添加到图层上,再将图层添加到地图上。

let feature = this.addPoints([118.958412, 32.119130]);
this.pointLayer.getSource().addFeature(feature);
this.map.addLayer(this.pointLayer);

feature = this.addPoints([118.948627, 32.120428]);
this.pointLayer.getSource().addFeature(feature);

3.4、给点初始化点击事件

思路就是遍历地图上所有图层中的Feature,并为它们添加点击事件。

关键代码:

initPointEvent(){
  let _that = this;
  //给点初始化点击事件
  this.map.on("singleclick", (e) => {
    let pixel = this.map.getEventPixel(e.originalEvent);
    let feature = this.map.forEachFeatureAtPixel(pixel, function(feature) { return feature; });
    if(feature){
      // 获取 Feature 的几何体
      const geometry = feature.getGeometry();
      // 获取坐标
      const coordinates = geometry.getCoordinates();
      // 更新 Overlay 位置
      _that.overlay.setPosition(coordinates);
      _that.overlay.getElement().style.display = 'block';
      let popupContent = document.getElementById('popup-content');
      popupContent.innerHTML = `<div>经度:${coordinates[0]}</div><div>纬度:${coordinates[1]}</div>`;
    }
  });
}

3.5、完整代码

<template>
  <div>
    <div id="map-container"></div>
    <div id="popup-box" class="popup-box">
      <button id="close-button" class="close-button">&times;</button>
      <div id="popup-content" class="popup-content"></div>
    </div>
  </div>
</template>
<script>
import {Feature, Map, View} from 'ol'
import { Tile as TileLayer } from 'ol/layer'
import { get } from 'ol/proj';
import { getWidth, getTopLeft } from 'ol/extent'
import { WMTS } from 'ol/source'
import WMTSTileGrid from 'ol/tilegrid/WMTS'
import { defaults as defaultControls} from 'ol/control';
import Overlay from 'ol/Overlay';
import {Point} from "ol/geom";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import {Icon, Style} from "ol/style";

export const projection = get("EPSG:4326");
const projectionExtent = projection.getExtent();
const size = getWidth(projectionExtent) / 256;
const resolutions = [];
for (let z = 0; z < 19; ++z) {
  resolutions[z] = size / Math.pow(2, z);
}

export default {
  data() {
    return {
      map:null,
      overLay:null,
      //所有点信息都放在这个图层
      pointLayer: new VectorLayer({
        source: new VectorSource(),
      }),
    }
  },
  mounted(){
    this.initMap() // 加载矢量底图
  },
  methods:{
    initMap() {
      const KEY = '你申请的KEY'
      this.map = new Map({
        target: 'map-container',
        layers: [
          // 底图
          new TileLayer({
            source: new WMTS({
              url: `http://t{0-6}.tianditu.com/vec_c/wmts?tk=${KEY}`,
              layer: 'vec', // 矢量底图
              matrixSet: 'c', // c: 经纬度投影 w: 球面墨卡托投影
              style: "default",
              crossOrigin: 'anonymous', // 解决跨域问题 如无该需求可不添加
              format: "tiles", //请求的图层格式,这里指定为瓦片格式
              wrapX: true, // 允许地图在 X 方向重复(环绕)
              tileGrid: new WMTSTileGrid({
                origin: getTopLeft(projectionExtent),
                resolutions: resolutions,
                matrixIds: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15','16','17','18']
              })
            })
          }),
          // 标注
          new TileLayer({
            source: new WMTS({
              url: `http://t{0-6}.tianditu.com/cva_c/wmts?tk=${KEY}`,
              layer: 'cva', //矢量注记
              matrixSet: 'c',
              style: "default",
              crossOrigin: 'anonymous',
              format: "tiles",
              wrapX: true,
              tileGrid: new WMTSTileGrid({
                origin: getTopLeft(projectionExtent),
                resolutions: resolutions,
                matrixIds: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15','16','17','18']
              })
            })
          })
        ],
        view: new View({
          center: [118.958366,32.119577],
          projection: projection,
          zoom: 12,
          maxZoom: 17,
          minZoom: 1
        }),
        //加载控件到地图容器中
        controls: defaultControls({
          zoom: false,
          rotate: false,
          attribution: false
        })
      });
      let popupBox = document.getElementById('popup-box');
      let closeButton = document.getElementById('close-button');
      //用于显示详情页面的窗口(根据经纬度来的,位置不固定)
      this.overlay = new Overlay({
        element: popupBox,
        autoPan: {
          animation: {
            duration: 250,
          },
        },
        offset:[0,-20],
      });
      this.map.addOverlay(this.overlay);
      // 关闭弹出框的事件处理
      let _that = this;
      closeButton.addEventListener('click', () => {
        _that.overlay.setPosition(undefined); // 关闭弹出框
      });


      let feature = this.addPoints([118.958412, 32.119130]);
      this.pointLayer.getSource().addFeature(feature);
      this.map.addLayer(this.pointLayer);

      feature = this.addPoints([118.948627, 32.120428]);
      this.pointLayer.getSource().addFeature(feature);

      this.initPointEvent();
    },
    /**
     * 根据经纬度坐标添加自定义图标 支持base64
     */
    addPoints(coordinate) {
      // 创建feature要素,一个feature就是一个点坐标信息
      let feature = new Feature({
        geometry: new Point(coordinate),
      });
      // 设置要素的图标
      feature.setStyle(
          new Style({
            // 设置图片效果
            image: new Icon({
              src: 'http://api.tianditu.gov.cn/img/map/markerA.png',
              // anchor: [0.5, 0.5],
              scale: 1,
            }),
          }),
      );
      return feature;
    },

    initPointEvent(){
      let _that = this;
      //给点初始化点击事件
      this.map.on("singleclick", (e) => {
        let pixel = this.map.getEventPixel(e.originalEvent);
        let feature = this.map.forEachFeatureAtPixel(pixel, function(feature) { return feature; });
        if(feature){
          // 获取 Feature 的几何体
          const geometry = feature.getGeometry();
          // 获取坐标
          const coordinates = geometry.getCoordinates();
          // 更新 Overlay 位置
          _that.overlay.setPosition(coordinates);
          _that.overlay.getElement().style.display = 'block';
          let popupContent = document.getElementById('popup-content');
          popupContent.innerHTML = `<div>经度:${coordinates[0]}</div><div>纬度:${coordinates[1]}</div>`;
        }
      });
    }
  }
}
</script>
<style scoped>

#map-container {
  width: 100%;
  height: 100vh;
}
.popup-box {
  background: rgba(255, 255, 255, 0.95);
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 20px;
  z-index: 1000;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
  transition: all 0.3s ease;
  max-width: 300px;
  font-family: 'Arial', sans-serif;
  position: absolute;
  transform: translate(-50%, -100%); /* 使弹出框上移并居中 */
}

/* 添加箭头样式 */
.popup-box::after {
  content: "";
  position: absolute;
  top: 100%; /* 箭头位于弹出框的底部 */
  left: 50%; /* 箭头横向居中 */
  margin-left: -6px; /* 调整箭头与弹出框的间距 */
  border-width: 6px; /* 箭头的大小 */
  border-style: solid;
  border-color: rgba(255, 255, 255, 0.95) transparent transparent transparent; /* 箭头的颜色 */
}

.close-button {
  background: none;
  color: gray;
  border: none;
  font-size: 20px;
  position: absolute;
  top: 10px;
  right: 10px;
  cursor: pointer;
}

.popup-content {
  width: 240px;
  margin-top: 10px;
  font-size: 16px;
  line-height: 1.5;
}
</style>

四、Gitee源码

地址: Vue2+OpenLayers给标点Feature添加信息窗体


http://www.kler.cn/a/505591.html

相关文章:

  • AI智能体实战|使用扣子Coze搭建AI智能体,看这一篇就够了(新手必读)
  • Onedrive精神分裂怎么办(有变更却不同步)
  • SpringMVC
  • 基于Python机器学习、深度学习技术提升气象、海洋、水文领域实践应用-以ENSO预测为例讲解
  • CMD批处理命令入门(5)——ping,ipconfig,arp,start,shutdown,taskkill
  • dockerfile2.0
  • 基于django中医药数据可视化平台(源码+lw+部署文档+讲解),源码可白嫖!
  • LeetCode热题100-二叉树的中序遍历【JavaScript讲解】
  • 11-1.Android 项目结构 - androidTest 包与 test 包(单元测试与仪器化测试)
  • 【C】数组和指针的关系
  • Ubuntu 安装和配置 MariaDB
  • 【行空板K10】上传温湿度信息到EasyIoT平台
  • redis闪退打不开Creating Server TCP listening socket *:6379: listen: Unknown error
  • ESP8266固件烧录
  • 利用Python爬虫按图搜索1688商品(拍立淘)的探索之旅
  • 从CRUD到高级功能:EF Core在.NET Core中全面应用(二)
  • 鸿蒙报错Init keystore failed: keystore password was incorrect
  • 【element plus】虚拟dom表格中cellRenderer如何使用v-for循环渲染item
  • 【vue3】 defineExpose 的使用
  • IIO(Industrial I/O)驱动介绍
  • 使用分割 Mask 和 K-means 聚类获取天空的颜色
  • 爬虫后的数据处理与使用(使用篇--实现分类预测)
  • css 三角构建
  • MCU中实时时钟(RTC)和普通定时器有什么区别
  • 深入Android架构(从线程到AIDL)_32 JNI架构原理_Java与C的对接05
  • C -- 大端对齐 小端对齐 的人性化解释