OpenLayers 地图标注之图文标注

news2025/6/4 8:32:18

注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key

地图标注是将空间位置信息点与地图关联、通过图标、窗口等形式把相关信息展现到地图上。在WebGIS中地图标注是重要的功能之一,可以为用户提供个性化服务,如兴趣点等。地图标注表现方式有图文标注、Popup标注、聚合标注等。本节主要介绍加载图文标注

1. 地图标注基本原理

地图标注通过在已知坐标点添加图片、文字或者图文的方式展现内容信息。可以通过鼠标点击获取目标点位置坐标,也可以通过属性传递获取。

2. 加载地图标注

OpenLayers地图标注由叠加类Overlay实现,主要内容参数为HTMLElement。示例通过document.createElement创建标注内容。

const htmlEle = document.createElement("div")
const closeEle = document.createElement('span')
const mainEle = document.createElement('div')

mainEle.className = 'ol-popup-main'
mainEle.textContent = "这是四川省"

closeEle.textContent = "x"
closeEle.className = "ol-popup-close"
htmlEle.className = "ol-popup"

htmlEle.appendChild(mainEle)
htmlEle.appendChild(closeEle)

通过map.addOverlay加载地图标注。

const marker = new ol.Overlay({
    id: "maker",
    position: chengdu,
    element: htmlEle,
    offset: [0, -70], // x、y轴偏移量,正值向右向下,负值相反
    positioning: 'top-center', // 定位方式,顶部居中
    aotuPan: true,
    autoPanMargin: 1.25,
})
marker.setProperties({ layerName: 'overlay' }) // 设置标注图层属性
map.addOverlay(marker)

3. 移除标注图层

可以根据图层名称或者传入标注图层进行移除。

function removeOverlayByName(layerName) {
    const overlays = map.getOverlays().getArray()
    // console.log("overlays:", overlays)
    overlays.forEach(layer => {
        console.log("layer.layerName:", layer.get('layerName'))
        if (layer.get('layerName') === layerName) map.removeOverlay(layer)
    });
}
function removeOverlayByLayer(overlay) {
    map.removeOverlay(overlay)
}

4. 打开和关闭Popup

OpenLayers中可以通过map.addOverlay实现Popup弹窗,在弹窗关闭之后,通过添加交互事件,在地图上点击目标要素打开PopupSelect类用于图层要素交互,可以通过Select.getFeatures()方法获取被选择的要素。

function openPopup(overlay) {
  map.addOverlay(overlay)
}

function selectFeature(layer) {
    const selectEvt = new ol.interaction.Select({
        layers: [layer], // 用于选择要素的图层
        style: new ol.style.Style({
            image: new ol.style.Circle({
                radius: 5,
                fill: new ol.style.Fill({
                    color: 'yellow'
                }),
                stroke: new ol.style.Stroke({
                    color: 'red'
                })
            })
        })
    })
    // 激活选择事件
    selectEvt.setActive(true)
    map.addInteraction(selectEvt)
    map.on('click', evt => {
        console.log("getLayers:", map.getLayers().getArray())
        const layers = map.getLayers().getArray()
        layers.forEach(layer => {
            const props = layer.getProperties()
            if (props.layerName === "pointLayer") {
                openPopup(marker)
            }
        })
    })
}

5. 完整代码

其中libs文件夹下的包需要更换为自己下载的本地包或者引用在线资源。

<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>图文标注</title>
  <meta charset="utf-8" />
  <script src="../libs/js/ol-5.3.3.js"></script>
  <script src="../libs/js/jquery-2.1.1.min.js"></script>
  <link rel="stylesheet" href="../libs/css//ol.css">
  <style>
      * {
          padding: 0;
          margin: 0;
          font-size: 14px;
          font-family: '微软雅黑';
      }

      html,
      body {
          width: 100%;
          height: 100%;
      }

      #map {
          position: absolute;
          width: 100%;
          height: 100%;
      }

      .ol-mouse-position {
          padding: 5px;
          top: 10px;
          height: 40px;
          line-height: 40px;
          background: #060505ba;
          text-align: center;
          color: #fff;
          border-radius: 5px;
      }

      .ol-popup {
          position: relative;
          font-size: 16px;
          color: #4c4c4c;
          background-color: #ddd;
          border-radius: 5px;
      }

      .ol-popup::before {
          display: block;
          content: "";
          width: 0;
          height: 0;
          border-left: 15px solid transparent;
          border-right: 15px solid transparent;
          border-top: 10px solid #ddd;
          position: absolute;
          bottom: -8px;
          left: 50%;
          transform: translateX(-50%);
      }

      .ol-popup-main {
          padding: 20px;
      }

      .ol-popup-close {
          position: absolute;
          display: inline-block;
          top: -6px;
          right: 5px;
          color: #878282b5;
          font-size: 20px;
      }

      .ol-popup-close:hover {
          cursor: pointer;
          color: #0e0e0eb5;
          filter: brightness(120%);
      }
  </style>
</head>

<body>
  <div id="map" title="地图显示"></div>
</body>

</html>

<script>
  //地图投影坐标系
  const projection = ol.proj.get('EPSG:3857');
  //==============================================================================//
  //============================天地图服务参数简单介绍==============================//
  //================================vec:矢量图层==================================//
  //================================img:影像图层==================================//
  //================================cva:注记图层==================================//
  //======================其中:_c表示经纬度投影,_w表示球面墨卡托投影================//
  //==============================================================================//
  const TDTImgLayer = new ol.layer.Tile({
      title: "天地图影像图层",
      source: new ol.source.XYZ({
          url: "http://t0.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=2a890fe711a79cafebca446a5447cfb2",
          attibutions: "天地图注记描述",
          crossOrigin: "anoymous",
          wrapX: false
      })
  })
  const TDTImgCvaLayer = new ol.layer.Tile({
      title: "天地图影像注记图层",
      source: new ol.source.XYZ({
          url: "http://t0.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=2a890fe711a79cafebca446a5447cfb2",
          attibutions: "天地图注记描述",
          crossOrigin: "anoymous",
          wrapX: false
      })
  })
  const map = new ol.Map({
      target: "map",
      loadTilesWhileInteracting: true,
      view: new ol.View({
          // center: [11421771, 4288300],
          // center: [102.6914059817791, 25.10595662891865],
          center: [104.0635986160487, 30.660919181071225],
          zoom: 10,
          worldsWrap: true,
          minZoom: 1,
          maxZoom: 20,
          projection: "EPSG:4326"
      }),
      // 鼠标控件:鼠标在地图上移动时显示坐标信息。
      controls: ol.control.defaults().extend([
          // 加载鼠标控件
          new ol.control.MousePosition()
      ])
  })

  map.addLayer(TDTImgLayer)
  map.addLayer(TDTImgCvaLayer)

  map.on('click', evt => {
    // 输出点击坐标
    console.log(evt.coordinate)
  })
  // 设置标注点
  const chengdu = [104.0635986160487, 30.660919181071225]

  // 添加点
  const pointLayer = new ol.layer.Vector({
      source: new ol.source.Vector({
          features: [
              new ol.Feature({
                  geometry: new ol.geom.Point(chengdu),
                  name: "chengdu"
              })
          ]
      }),
      style: new ol.style.Style({
          image: new ol.style.Circle({
              radius: 5,
              fill: new ol.style.Fill({
                  color: 'blue'
              })
          }),
          fill: new ol.style.Fill({
              color: "yellow"
          })
      })
  })
  pointLayer.setProperties({ "layerName": "pointLayer" })
  map.addLayer(pointLayer)

  const htmlEle = document.createElement("div")
  const closeEle = document.createElement('span')
  const mainEle = document.createElement('div')

  htmlEle.className = "ol-popup"
  mainEle.className = 'ol-popup-main'
  closeEle.className = "ol-popup-close"

  mainEle.textContent = "这是四川省"
  closeEle.textContent = "x"

  htmlEle.appendChild(mainEle)
  htmlEle.appendChild(closeEle)

  // 注册关闭popup事件
  closeEle.addEventListener('click', evt => {
      removeOverlayByName("overlay")
      // removeOverlayByLayer(marker)
  })
  const marker = new ol.Overlay({
      id: "maker",
      position: chengdu,
      element: htmlEle,
      offset: [0, -70], // x、y轴偏移量,正值向右向下,负值相反
      positioning: 'top-center', // 定位方式,顶部居中
      aotuPan: true,
      autoPanMargin: 1.25,
  })
  marker.setProperties({ layerName: 'overlay' })
  map.addOverlay(marker)

  selectFeature(pointLayer)

  function removeOverlayByName(layerName) {
      const overlays = map.getOverlays().getArray()
      // console.log("overlays:", overlays)
      overlays.forEach(layer => {
          console.log("layer.layerName:", layer.get('layerName'))
          if (layer.get('layerName') === layerName) map.removeOverlay(layer)
      });
  }
  function removeOverlayByLayer(overlay) {
      map.removeOverlay(overlay)
  }

  function openPopup(overlay) {
      map.addOverlay(overlay)
  }

  /**
   * pointLayer 点击监听事件
   * openlayers 中要素点击事件通过ol.interaction.Select实现,
   * 通过 getFeatures() 获取点击的要素
   */
  function selectFeature(layer) {
      const selectEvt = new ol.interaction.Select({
          layers: [layer], // 用于选择要素的图层
          style: new ol.style.Style({
              image: new ol.style.Circle({
                  radius: 5,
                  fill: new ol.style.Fill({
                      color: 'yellow'
                  }),
                  stroke: new ol.style.Stroke({
                      color: 'red'
                  })
              })
          })
      })
      // 激活选择事件
      selectEvt.setActive(true)
      map.addInteraction(selectEvt)
      map.on('click', evt => {
          console.log("getLayers:", map.getLayers().getArray())
          const layers = map.getLayers().getArray()
          layers.forEach(layer => {
              const props = layer.getProperties()
              if (props.layerName === "pointLayer") {
                  openPopup(marker)
              }
          })
          // 获取选择要素
          // const features = selectEvt.getFeatures().getArray()
          // console.log("feats:", features)
          // 若点击点要素,则打开Popup
          // if (features.length) {
          //     openPopup(marker)
          // }

          // 监听状态
          // selectEvt.on('change:active', evt => {
          //    features.forEach(features.remove, features)   
          // })
      })

  }
</script>

OpenLayers示例数据下载,请回复关键字:ol数据

全国信息化工程师-GIS 应用水平考试资料,请回复关键字:GIS考试

【GIS之路】 已经接入了智能助手,欢迎关注,欢迎提问。

欢迎访问我的博客网站-长谈GIShttp://shanhaitalk.com

都看到这了,不要忘记点赞、收藏 + 关注

本号不定时更新有关 GIS开发 相关内容,欢迎关注 !

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2395715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式——简单工厂模式(创建型)

摘要 本文主要介绍了简单工厂模式&#xff0c;包括其定义、结构、实现方式、适用场景、实战示例以及思考。简单工厂模式是一种创建型设计模式&#xff0c;通过工厂类根据参数决定创建哪一种产品类的实例&#xff0c;封装了对象创建的细节&#xff0c;使客户端无需关心具体类的…

qt ubuntu 20.04 交叉编译

一、交叉编译环境搭建 1.下载交叉编译工具链&#xff1a;https://developer.arm.com/downloads/-/gnu-a 可以根据自己需要下载对应版本&#xff0c;当前最新版本是10.3, 笔者使用10.3编译后的glibc.so版本太高&#xff08;glibc_2.3.3, glibc_2.3.4, glibc_2.3.5&#xff09;…

晶振频率稳定性:5G 基站与航天设备的核心竞争力

在当今科技飞速发展的时代&#xff0c;电子设备的性能和可靠性至关重要。晶振作为电子设备中的核心部件&#xff0c;为系统提供精确的时间和频率基准。晶振的频率稳定性直接影响着设备的整体性能&#xff0c;从日常生活中广泛使用的智能手机、智能穿戴设备&#xff0c;到对精度…

基于python脚本进行Maxwell自动化仿真

本文为博主进行Maxwell自动化研究过程的学习记录&#xff0c;同时对Maxwell自动化脚本&#xff08;pythonIron&#xff09;实现方法进行分享。 文章目录 脚本使用方法脚本录制与查看常用脚本代码通用开头定义项目调整设计变量软件内对应位置脚本 设置求解器软件内对应位置脚本…

Blueprints - List View Widget

一些学习笔记归档&#xff1b; 需要读取动态数据把多个条目显示在UI上的时候&#xff0c;可能用到List View组件&#xff1b;假如有Widget要使用在List View中&#xff0c;此Widget需要继承相关接口&#xff1a; 这样就能在List View控件中选择已经继承接口的Widget组件了&…

docker-compose搭建prometheus以及grafana

1. 什么是 Prometheus&#xff1f; Prometheus 是一个开源的系统监控和告警工具&#xff0c;由 SoundCloud 于 2012 年开始开发&#xff0c;现为 CNCF&#xff08;Cloud Native Computing Foundation&#xff09;项目之一。它特别适合云原生环境和容器编排系统&#xff08;如 …

多线程和并发之线程

线程 前面讲到进程&#xff1a;为了并发执行任务&#xff08;程序&#xff09;&#xff0c;现代操作系统才引进进程的概念 分析&#xff1a; 创建开销问题&#xff1a;创建一个进程开销&#xff1a;大 子进程需要拷贝父进程的整个地址空间 通信开销问题&#xff1a;进程间的通…

apptrace 的优势以及对 App 的价值

官网地址&#xff1a;AppTrace - 专业的移动应用推广追踪平台 apptrace 的优势以及对 App 的价值​ App 拉起作为移动端深度链接技术的关键应用&#xff0c;能实现从 H5 网页到 App 的无缝跳转&#xff0c;并精准定位到 App 内指定页面。apptrace 凭借专业的技术与丰富的经验…

android studio debug调试出现 IOException异常

解决Android调试端口无法打开的问题&#xff0c;出现"Unable to open debugger port"错误时&#xff0c;可以进入app设置&#xff0c;选择Debugger选项&#xff0c;将Debug type更改为Java Only模式。这个方法适用于Android Studio调试时遇到的端口连接问题&#xff…

vr中风--数据处理模型搭建与训练

# -*- coding: utf-8 -*- """ MUSED-I康复评估系统&#xff08;增强版&#xff09; 包含&#xff1a;多通道sEMG数据增强、混合模型架构、标准化处理 """ import numpy as np import pandas as pd from sklearn.model_selection import train_te…

前端学习(7)—— HTML + CSS实现博客系统页面

目录 一&#xff0c;效果展示 二&#xff0c;实现博客列表页 2.1 实现导航栏 2.2 实现个人信息 2.3 实现博客列表 三&#xff0c;实现博客正文页 3.2 复用 3.4 实现博客正文 四&#xff0c;实现博客登录页 4.1 版心 4.2 登录框 五&#xff0c;实现博客编辑页 5.1 …

RuoYi前后端分离框架实现前后端数据传输加密(二)之前端篇

一、背景 本文是RuoYi前后端分离框架实现前后端数据传输加密(一)之后端篇文章配套的,主要介绍前端对自定义字段传输加密的实现,两篇文章结合可以完整的完成RuoYi前后端分离框架对API通信过程中实现自定义字段加密传输。前端的加解密实现,不涉及到界面的修改,仅仅是方法的…

基于视觉的车道线检测完整代码:让驾驶更安全的开源解决方案

基于视觉的车道线检测完整代码&#xff1a;让驾驶更安全的开源解决方案 【下载地址】基于视觉的车道线检测完整代码 这是一个基于视觉的车道线检测开源项目&#xff0c;提供完整的代码示例&#xff0c;采用滑动窗口算法实现。项目通过逐行扫描图像&#xff0c;精准识别曲线车道…

鸿蒙仓颉开发语言实战教程:自定义tabbar

大家周末好呀&#xff0c;今天继续分享仓颉语言开发商城应用的实战教程&#xff0c;今天要做的是tabbar。 大家都知道ArkTs有Tabs和TabContent容器&#xff0c;能够实现上图的样式&#xff0c;满足基本的使用需求。而仓颉就不同了&#xff0c;它虽然也有这两个组件&#xff0c;…

28 C 语言作用域详解:作用域特性(全局、局部、块级)、应用场景、注意事项

1 作用域简介 作用域定义了代码中标识符&#xff08;如变量、常量、数组、函数等&#xff09;的可见性与可访问范围&#xff0c;即标识符在程序的哪些位置能够被引用或访问。在 C 语言中&#xff0c;作用域主要分为三类&#xff1a; 全局作用域局部作用域块级作用域 需注意&am…

MySQL 事务解析

1. 事务简介 事务&#xff08;Transaction&#xff09; 是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 经典案例&#xff1…

题海拾贝:压缩字符串

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 欢迎点赞&#xff0c;关注&am…

振动力学的三类基本问题

振动问题的分类依赖于分类的出发点&#xff0c;本文从系统论的角度来分析振动问题的分类。如图1&#xff0c;一个振动系统&#xff0c;包括三个方面&#xff1a;输入、系统特性&#xff08;或称为系统模型&#xff09;、输出。其中&#xff0c;输入指外界载荷&#xff0c;包括力…

移动端 UI自动化测试学习之Appium框架(包含adb调试工具介绍)

文章目录 前言adb调试工具adb组成常用命令获取程序的包名和界面名文件传输发送文件到手机从手机中拉取文件 获取app启动时间获取手机日志其他命令 Appium 简介工作原理图 环境搭建安装客户端库&#xff08;appium lib&#xff09;安装Appium Server安装JDK&#xff08;自行下载…

CS144 - Lecture 2

CS144 - Lecture 1 TCP 这里就简单讲了一下它的基本性质&#xff0c;没啥好说的 UDP 提供不可靠的传输服务&#xff0c;我们的 DNS 服务和 DHCP 都是用的 UDP 协议。 对于 DNS 我们只是单纯地向 DNS 服务器发送域名&#xff0c;然后返回一个 IP&#xff0c;如果还需要建立…