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

OpenLayers 图文标注大全

前言

要素标注是一个常用功能,就是将要素信息如名称等显示在地图上,方便查找和使用。在OpenLayers中要素标注很灵活,使用起来也很方便,可以修改标注样式以及标注权限。

在本文中主要介绍图文标注详细信息,包括旋转缩放和偏移等。

1. 创建标注点及样式

在创建标注点之前需要先获取位置坐标数据,通过监听map对象单击事件可以得到点坐标数据。

map.on('click', evt => {console.log("获取地图坐标:", evt.coordinate)
})

还需要提前准备好图片数据,然后根据坐标点创建几何对象,再由几何对象创建点要素,最后创建要素样式和点图层。对于要素类对象,可以通过setStyle方法重写图层样式,该方法参数可以是单个样式对象,也可以是样式数组对象或者接受分辨率并返回样式数组的函数。最后设置标注点为视图中心。

const pointGeometry = new ol.geom.Point([100.9788433214299, 26.747704222514738])
const feature = new ol.Feature({geometry: pointGeometry
})
const vectotSource = new ol.source.Vector({features: [feature],wrapX: false
})
const iconStyle = new ol.style.Style({image: new ol.style.Icon({src: "../../image/label/simple_map.jpg",scale: [1 / 16, 1 / 16],// anchor: [0.5, 1.25]}),text: new ol.style.Text({font: "16px sans-serif",textAlign: "center",text: "Hello,World",fill: new ol.style.Fill({color: [255, 215, 0, 1],}),backgroundFill: new ol.style.Fill({color: [0, 191, 255, 0.25]}),offsetY: -30, // 向Y轴偏移padding: [10, 10, 10, 10]})
})
const featureStyle = new ol.style.Style({image: new ol.style.Circle({radius: 10,fill: new ol.style.Fill({color: "#fff"}),stroke: new ol.style.Stroke({color: "red"})})
})
feature.setStyle([iconStyle, featureStyle])
const vectorLayer = new ol.layer.Vector({source: vectotSource
})
map.addLayer(vectorLayer)
// 设置标注点为视图中心
map.getView().setCenter(pointGeometry.getCoordinates())

2. 视图旋转

监听滑块事件,设置视图旋转。setRotation参数接收弧度值,所以需要将度数转换为弧度。

// 视图旋转事件
slider.render({// 顺时针旋转,弧度单位elem: '#view-rotate',value: 0,min: 0,max: 360,step: 5,change: function (value) {map.getView().setRotation(value * Math.PI / 180)}
});

**监听图片文本跟随视图旋转事件:**监听开关事件,并在值改变时通过setRotateWithView方法设置是否开启跟随视图旋转。该方法接收参数为布尔值。

// 图片跟随视图旋转事件
form.on('switch(image-box-filter)', function (data) {const elem = data.elem; // 获得 checkbox 原始 DOM 对象const checked = elem.checked; // 获得 checkbox 选中状态iconStyle.getImage().setRotateWithView(checked)
});
// 文本跟随视图旋转事件
form.on('switch(text-box-filter)', function (data) {const elem = data.elem; // 获得 checkbox 原始 DOM 对象const checked = elem.checked; // 获得 checkbox 选中状态iconStyle.getText().setRotateWithView(checked)
});

3. 监听图片文本事件

layui组件中调用slider.render方法创建滑块,然后配置最大最小值等参数,在change事件中,监听滑块值变化并更改要素标注样式。注意:在改变要素样式后,要素对象需立即调用changed方法通知Feature,说明样式已经被改变了。**layui**滑块配置[https://layui.dev/docs/2/slider/](https://layui.dev/docs/2/slider/)

// 图片旋转事件
slider.render({// 顺时针旋转,弧度单位elem: '#imageRotation',value: 0,min: 0,max: 360,step: 5,change: function (value) {iconStyle.getImage().setRotation(value * Math.PI / 180)feature.changed()}
});
// 文本旋转事件
slider.render({// 顺时针旋转,弧度单位elem: '#textRotation',value: 0,min: 0,max: 360,step: 5,change: function (value) {iconStyle.getText().setRotation(value * Math.PI / 180)feature.changed()}
});

4. 属性介绍

4.1. Icon

Icon类用于设置要素类图标样式。其属性信息如下表。

属性名

类型

描述

anchor

Array.<number> (defaults to [0.5, 0.5])

锚点,默认值图标中心

anchorOrigin

IconOrigin (defaults to 'top-left')

锚点原点: bottom-left, bottom-right, top-left or top-right

anchorXUnits

IconAnchorUnits (defaults to 'fraction')

X锚点单位,可以是分数或者像素

anchorYUnits

IconAnchorUnits (defaults to 'fraction')

Y锚点单位,可以是分数或者像素

color

Color | string | undefined

设置图片颜色

img

HTMLImageElement | HTMLCanvasElement | ImageBitmap | undefined

图标图片对象

displacement

Array.<number> (defaults to [0, 0])

图标位移像素。正值将使图标向右和向上移动。

opacity

number (defaults to 1)

图标透明度

width

number | undefined

图标宽度(像素),不能与scale一起使用

height

number | undefined

图标高度(像素),不能与scale一起使用

scale

number | Size (defaults to 1)

缩放比例

rotateWithView

boolean (defaults to false)

是否跟随视图旋转

rotation

number (defaults to 0)

旋转(弧度)

offset

Array.<number> (defaults to [0, 0])

偏移

offsetOrigin

IconOrigin

(defaults to 'top-left')

偏移原点: bottom-left, bottom-right, top-left or top-right.

size

Size | undefined

图标大小(像素)

src

string | undefined

图片url

declutterMode

DeclutterMode | undefined

分离模式(压盖处理)

4.2. Text

Text类为矢量要素设置文本样式。其属性信息如下表。

属性名

类型

描述

font

string | undefined

css 字体样式值

maxAngle

number (defaults to Math.PI/4)

当放置设置为“行”时,允许相邻字符之间的最大角度。预期值以弧度为单位,默认值为45°

offsetX

number (defaults to 0)

水平偏移量(像素),正值向右

offsetY

number (defaults to 0)

竖直偏移量(像素),正值向下

overflow

boolean (defaults to false)

对于多边形标签或当放置设置为“线”时,允许文本超过标签位置处多边形的宽度或其遵循的路径的长度。

placement

TextPlacement (defaults to 'point')

文本方式模式

repeat

number | undefined

重复间隔。设置后,文本将以该间隔重复,该间隔指定了两个文本锚点之间的像素距离。仅当放置设置为“行”时可用。覆盖“textAlign”。

scale

number | Size | undefined

缩放比例

rotateWithView

boolean (defaults to false)

是否跟随视图旋转

rotation

number (defaults to 0)

旋转(弧度),正值顺时针方向

text

string | Array.<string> | undefined

文本内容

textAlign

CanvasTextAlign | undefined

文本框对齐方式

justify

TextJustify | undefined

文本框内文本对齐方式

textBaseline

CanvasTextBaseline (defaults to 'middle')

文本基线

fill

Fill | null | undefined

文本填充样式

stroke

Stroke | undefined

文本描边样式

backgroundFill

Fill | undefined

文本框背景颜色

backgroundStroke

Stroke | undefined

文本框背景描边颜色

padding

Array.<number> (defaults to [0, 0, 0, 0])

文本框内边距

declutterMode

DeclutterMode | undefined

文本压盖模式

5. 完整代码

其中libs文件夹下的包需要更换为自己下载的本地包或者引用在线资源。本示例引用了layui组件,请自行替换。

<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>OpenLayers 图文标注大全</title><meta charset="utf-8" /><link rel="stylesheet" href="../../libs/css/ol9.2.4.css"><link rel="stylesheet" href="../../libs/layui/css/layui.css"><script src="../../js/config.js"></script><script src="../../libs/js/proj4.js"></script><script src="../../libs/js/ol9.2.4.js"></script><script src="../../libs/layui/layui.js"></script><script src="../../libs/js/geotiff.min.js"></script><style>* {padding: 0;margin: 0;font-size: 14px;font-family: '微软雅黑';}html,body {width: 100%;height: 100%;}#map {position: absolute;top: 50px;bottom: 0;width: 100%;}#top-content {position: absolute;width: 100%;height: 50px;line-height: 50px;background: linear-gradient(135deg, #ff00cc, #ffcc00, #00ffcc, #ff0066);color: #fff;text-align: center;font-size: 32px;}#top-content span {font-size: 32px;}#layer-container {position: absolute;top: 10%;left: 20px;width: 30%;bottom: 5%;background: #fff;border-radius: 2.5px;border: 1px solid #ddd;overflow-y: scroll;max-height: 80%;}.layer-head {background: #16baaa;padding: 10px;margin-bottom: 15px;}.label-style {}.image-style-content {margin: 5px 25px;}.text-title {padding: 10px;margin-bottom: 15px;background: #16baaa;color: #fff;}.image-title {padding: 10px;margin-bottom: 15px;background: #16baaa;color: #fff;}.layer-prop-item {display: flex;justify-content: space-between;flex-direction: row;margin: 10px 0;}.layer-slider-item {width: 45%;margin-top: 16px;}.layui-form-label {width: 50%;padding: 8px 15px;height: 38px;line-height: 20px;border-width: 1px;border-style: solid;border-radius: 2px 0 0 2px;text-align: center;background-color: #fafafa;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;box-sizing: border-box;border-color: #eee;font-weight: 400;}.map-rotate {position: absolute;right: 20px;top: 100px;padding: 10px 20px;width: 250px;background: #fff;border-radius: 5px;box-shadow: 5px 6px 6px 2px #29292975;}.view-slider-item {margin-top: 8px;width: 70%;}</style>
</head><body><div id="top-content"><span>OpenLayers 图文标注大全</span></div><div id="map" title="地图显示"></div><div class="map-rotate"><div class='layer-prop-item'><label class="layui-label">旋转地图:</label><div class="view-slider-item" id="view-rotate"></div></div></div><div id="layer-container"><!-- <h2 class="layer-head">样式属性</h2> --><div class="label-style"><div class="image-style"><h3 for="" class="image-title">Image Style</h3><div class="image-style-content"><div class='layer-prop-item layui-form'><label class="layui-form-label">跟随视图旋转</label><input type="checkbox" name="image-rotation" lay-filter="image-box-filter" title="开启|关闭"lay-skin="switch"></div><div class='layer-prop-item'><label class="layui-form-label">图片旋转(度)</label><div class="layer-slider-item" id="imageRotation"></div></div><div class='layer-prop-item'><label class="layui-form-label">水平缩放</label><div class="layer-slider-item" id="imageScaleX"></div></div><div class='layer-prop-item'><label class="layui-form-label">竖直缩放</label><div class="layer-slider-item" id="imageScaleY"></div></div><div class='layer-prop-item'><label class="layui-form-label">水平位置</label><div class="layer-slider-item" id="imageAnchorX"></div></div><div class='layer-prop-item'><label class="layui-form-label">竖直位置</label><div class="layer-slider-item" id="imageAnchorY"></div></div><div class='layer-prop-item'><label class="layui-form-label">水平偏移</label><div class="layer-slider-item" id="imageDisplacementX"></div></div><div class='layer-prop-item'><label class="layui-form-label">竖直偏移</label><div class="layer-slider-item" id="imageDisplacementY"></div></div></div></div><div class="text-style"><h3 for="" class="text-title">Text Style</h3><div class="image-style-content"><div class='layer-prop-item layui-form'><label class="layui-form-label">跟随视图旋转</label><input type="checkbox" name="text-rotation" lay-filter="text-box-filter" title="开启|关闭"lay-skin="switch"></div><div class='layer-prop-item'><label class="layui-form-label">文本旋转(度)</label><div class="layer-slider-item" id="textRotation"></div></div><div class='layer-prop-item'><label class="layui-form-label">水平缩放</label><div class="layer-slider-item" id="textScaleX"></div></div><div class='layer-prop-item'><label class="layui-form-label">竖直缩放</label><div class="layer-slider-item" id="textScaleY"></div></div><div class='layer-prop-item '><label class="layui-form-label">文本对齐方式</label><div class="layui-col-md6 layui-form layui-row layui-col-space16"><select lay-filter="align-select-filter"><option value="left">居左</option><option value="center">居中</option><option value="right">居右</option><option value="start">开始</option><option value="end">结束</option></select></div></div><div class='layer-prop-item'><label class="layui-form-label">文本基线</label><div class="layui-col-md6 layui-form layui-row layui-col-space16"><select lay-filter="baseline-select-filter"><option value="top">top</option><option value="bottom">bottom</option><option value="middle">middle</option><option value="alphabetic">alphabetic</option><option value="hanging">hanging</option><option value="ideographic">ideographic</option></select></div></div><div class='layer-prop-item'><label class="layui-form-label">水平偏移</label><div class="layer-slider-item" id="textOffsetX"></div></div><div class='layer-prop-item'><label class="layui-form-label">竖直偏移</label><div class="layer-slider-item" id="textOffsetY"></div></div></div></div></div></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=" + TDTTOKEN,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=" + TDTTOKEN,attibutions: "天地图注记描述",crossOrigin: "anoymous",wrapX: false})})const map = new ol.Map({target: "map",loadTilesWhileInteracting: true,view: new ol.View({center: [102.845864, 25.421639],zoom: 6.5,worldsWrap: false,minZoom: 1,maxZoom: 20,projection: 'EPSG:4326',}),layers: [TDTImgLayer],// 地图默认控件controls: ol.control.defaults.defaults({zoom: false,attribution: true,rotate: true})})map.on('click', evt => {console.log("获取地图坐标:", evt.coordinate)})const pointGeometry = new ol.geom.Point([100.9788433214299, 26.747704222514738])const feature = new ol.Feature({geometry: pointGeometry})const vectotSource = new ol.source.Vector({features: [feature],wrapX: false})const iconStyle = new ol.style.Style({image: new ol.style.Icon({src: "../../image/label/simple_map.jpg",scale: [1 / 16, 1 / 16],// color: "red"// anchor: [0.5, 1.25]}),text: new ol.style.Text({font: "16px sans-serif",textAlign: "center",text: "Hello,World",fill: new ol.style.Fill({color: [255, 215, 0, 1],}),backgroundFill: new ol.style.Fill({color: [0, 191, 255, 0.25]}),offsetY: -30, // 向Y轴偏移padding: [10, 10, 10, 10]})})const featureStyle = new ol.style.Style({image: new ol.style.Circle({radius: 10,fill: new ol.style.Fill({color: "#fff"}),stroke: new ol.style.Stroke({color: "red"})})})feature.setStyle([iconStyle, featureStyle])const vectorLayer = new ol.layer.Vector({source: vectotSource})map.addLayer(vectorLayer)map.getView().setCenter(pointGeometry.getCoordinates())layui.use(function () {const form = layui.form;const layer = layui.layerconst imageDisplacement = iconStyle.getImage().getDisplacement()console.log("imageDisplacement:", imageDisplacement)// 图片跟随视图旋转事件form.on('switch(image-box-filter)', function (data) {const elem = data.elem; // 获得 checkbox 原始 DOM 对象const checked = elem.checked; // 获得 checkbox 选中状态const value = elem.value; // 获得 checkbox 值iconStyle.getImage().setRotateWithView(checked)});// 文本跟随视图旋转事件form.on('switch(text-box-filter)', function (data) {const elem = data.elem; // 获得 checkbox 原始 DOM 对象const checked = elem.checked; // 获得 checkbox 选中状态const value = elem.value; // 获得 checkbox 值iconStyle.getText().setRotateWithView(checked)});// select 文本事件form.on('select(align-select-filter)', function (data) {const elem = data.elem; // 获得 select 原始 DOM 对象const value = data.value; // 获得被选中的值const othis = data.othis; // 获得 select 元素被替换后的 jQuery 对象iconStyle.getText().setTextAlign(value)feature.changed()});// select 文本基线事件form.on('select(baseline-select-filter)', function (data) {const elem = data.elem; // 获得 select 原始 DOM 对象const value = data.value; // 获得被选中的值const othis = data.othis; // 获得 select 元素被替换后的 jQuery 对象iconStyle.getText().setTextBaseline(value)feature.changed()});const slider = layui.slider;// 视图旋转事件slider.render({// 顺时针旋转,弧度单位elem: '#view-rotate',value: 0,min: 0,max: 360,step: 5,change: function (value) {// console.log(value) // 滑块当前值map.getView().setRotation(value * Math.PI / 180)}});// 图片渲染事件slider.render({// 顺时针旋转,弧度单位elem: '#imageRotation',value: 0,min: 0,max: 360,step: 5,change: function (value) {console.log(value) // 滑块当前值iconStyle.getImage().setRotation(value * Math.PI / 180)feature.changed()}});slider.render({elem: '#imageScaleX',value: 1,min: -2,max: 2,step: 0.25,change: function (value) {// console.log(value) // 滑块当前值iconStyle.getImage().setScale([value * 1 / 16, 1 / 16])feature.changed()}});slider.render({elem: '#imageScaleY',value: 1,min: -2,max: 2,step: 0.25,change: function (value) {// console.log(value) // 滑块当前值iconStyle.getImage().setScale([1 / 16, value * 1 / 16])feature.changed()}});slider.render({elem: '#imageAnchorX',value: 0,min: -2,max: 2,step: 0.125,change: function (value) {// console.log(value) // 滑块当前值iconStyle.getImage().setAnchor([0.5 + value, 1.25])feature.changed()}});slider.render({elem: '#imageAnchorY',value: 0,min: -2,max: 2,step: 0.125,change: function (value) {// console.log(value) // 滑块当前值iconStyle.getImage().setAnchor([0.5, 1.25 + value])feature.changed()}});slider.render({elem: '#imageDisplacementX',value: 0,min: -50,max: 50,step: 1,change: function (value) {// console.log(value) // 滑块当前值iconStyle.getImage().setDisplacement([value, 0])feature.changed()}});slider.render({elem: '#imageDisplacementY',value: 0,min: -50,max: 50,step: 1,change: function (value) {// console.log(value) // 滑块当前值iconStyle.getImage().setDisplacement([0, value])feature.changed()}});slider.render({// 顺时针旋转,弧度单位elem: '#textRotation',value: 0,min: 0,max: 360,step: 5,change: function (value) {console.log(value) // 滑块当前值iconStyle.getText().setRotation(value * Math.PI / 180)feature.changed()}});slider.render({elem: '#textScaleX',value: 1,min: -2,max: 2,step: 0.25,change: function (value) {// console.log(value) // 滑块当前值const scaleY = iconStyle.getText().getScale()const curScaleY = scaleY ? +scaleY[1] : 1iconStyle.getText().setScale([value, curScaleY])feature.changed()}});slider.render({elem: '#textScaleY',value: 1,min: -2,max: 2,step: 0.25,change: function (value) {// console.log(value) // 滑块当前值const scaleX = iconStyle.getText().getScale()const curScaleX = scaleX ? +scaleX[0] : 1iconStyle.getText().setScale([curScaleX, value])feature.changed()}});slider.render({elem: '#textOffsetX',value: 0,min: -200,max: 200,step: 5,change: function (value) {// console.log(value) // 滑块当前值iconStyle.getText().setOffsetX(value)feature.changed()}});slider.render({elem: '#textOffsetY',value: 0,min: -200,max: 200,step: 5,change: function (value) {// console.log(value) // 滑块当前值iconStyle.getText().setOffsetY(value)feature.changed()}});});</script>

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

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

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

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

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

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

相关文章:

  • 哪个网站可以找题目给小孩做seo外包公司兴田德润
  • 代理网页 在线西安网站seo价格
  • 在深圳做网站平台需要什么备案/杭州优化建筑设计
  • 平台建站/抖音seo优化系统招商
  • 云游戏网站在线玩/百度集团
  • b2b网站推广方法/百家联盟推广部电话多少
  • springcloud/springmvc协调作用传递验证信息
  • 什么是Sentinel?以及优缺点
  • 手撕 Decoder
  • Day 2:Shell变量解密——从“Hello World“到会“记忆“的脚本
  • C语言数组介绍 -- 一维数组和二维数组的创建、初始化、下标、遍历、存储,C99 变长数组
  • Zynq + FreeRTOS + YAFFS2 + SQLite3 集成指南
  • python计算长方形的周长 2025年3月青少年电子学会等级考试 中小学生python编程等级考试一级真题答案解析
  • Vibe Coding - 使用cursor从PRD到TASK精准分解执行
  • 《内心强大不怯场》读书笔记3
  • 智能营销系统对企业的应用价值
  • 【Java面试】你是怎么控制缓存的更新?
  • Linux内核网络栈的智慧:skb->cb控制缓冲区的设计哲学
  • sudo安装pip包的影响
  • 有哪些词编码模型
  • 相机标定与3D重建技术通俗讲解
  • Python基础(​​FAISS​和​​Chroma​)
  • 每日算法刷题Day36 6.23:leetcode枚举技巧枚举中间4道题,用时1h30min
  • VLN论文复现——VLFM(ICRA最佳论文)
  • 【图像】ubuntu中图像处理
  • 可编辑精品PPT | 企业数字化商业平台客户中台解决方案客户CRM数据中台方案