❝
注:当前使用的是 ol 5.3.0 版本,天地图使用的
key
请到天地图官网申请,并替换为自己的key
地图标注是将空间位置信息点与地图关联、通过图标、窗口等形式把相关信息展现到地图上。在 我WebGIS
中地图标注是重要的功能之一,可以为用户提供个性化服务,如兴趣点等。地图标注表现方式有图文标注、Popup
标注、聚合标注等。本节主要介绍加载Popup标注。
1. 地图标注基本原理
地图标注通过在已知坐标点添加图片、文字或者图文的方式展现内容信息。可以通过鼠标点击获取目标点位置坐标,也可以通过属性传递获取。
2. 创建图标标注以及Popup加载图片
在图标网站如[https://www.iconfont.cn/](https://www.iconfont.cn/)
下载定位图片,搜索框输入locate
寻找适合的图片。创建图标样式如下,调整图标显示位置anchor
,图标大小size
以及图片路径src
。
php
// 添加图片标注
const imageStyle = new ol.style.Style({
image: new ol.style.Icon({
anchor: [32, 10], // 调整图标位置,正值向左向下,负值相反
anchorOrigin: "bottom-left", // 图标位置起始位置,默认'top-left'
anchorXUnits: 'pixels',
anchorYUnits: 'pixels',
offsetOrigin: 'top-left', // 图标位置偏移起始位置,默认'top-left'
opacity: 0.75,
size: [64, 64], // 图片大小
src: './locate.png' // 图片路径
}),
text: new ol.style.Text({
font: "normal 14px"
})
})
在地图上添加标注点。
php
// 添加标注点
const pointLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [
new ol.Feature({
geometry: new ol.geom.Point(chengdu),
name: "chengdu",
properties: {
province: "四川省", region: "成都市", population: "2140.3万人"
}
}),
new ol.Feature({
geometry: new ol.geom.Point(xjq),
name: "xjq",
properties: {
province: "四川省", region: "新津区", population: "36.36万人"
}
})
]
}),
style: imageStyle
})
pointLayer.setProperties({ "layerName": "pointLayer" })
map.addLayer(pointLayer)
3. 创建Popup内容
创建函数createPopupEle
,通过传入HTML
元素或者描述文字生成Popup HTML
。
ini
/**
* 创建Popup 内容
* @mainContent:HTML 或者 文字
*/
function createPopupEle(mainContent) {
const htmlEle = document.createElement("div")
const closeEle = document.createElement('span')
const mainEle = document.createElement('div')
mainEle.className = 'ol-popup-main'
console.log(typeof mainContent)
const contentType = typeof mainContent
if (contentType === 'object') {
mainEle.appendChild(mainContent)
} else if (contentType === 'string') {
mainEle.innerHTML = mainContent
}
closeEle.textContent = "×"
closeEle.className = "ol-popup-close"
htmlEle.className = "ol-popup"
htmlEle.appendChild(mainEle)
htmlEle.appendChild(closeEle)
// 注册关闭popup事件
closeEle.addEventListener('click', evt => {
removeOverlayByName("overlay")
// removeOverlayByLayer(marker)
})
return htmlEle
}
4. 创建叠加图层
在OpenLayers
中,Popup
弹窗的实现通过创建叠加图层实现。在函数createOvrlay
中传入Popup HTML
元素以及定位Position
,并将其添加到地图中。
php
/**
* 创建Overlay
* @popupEle:popup html 结构
* @position:popup 定位坐标,形式为 [longitude,latitude]
*/
function createOvrlay(popupEle, position) {
const marker = new ol.Overlay({
id: "maker",
position: position,
element: popupEle,
offset: [0, -30], // x、y轴偏移量,正值向右向下,负值相反
positioning: 'bottom-center', // 定位方式,顶部居中
aotuPan: true,
autoPanMargin: 1.25,
})
marker.setProperties({ layerName: 'overlay' })
return marker
}
5. 打开和关闭Popup
打开和关闭Popup
相对简单,只需要调用map
对象的addOverlay
方法进行加载,removeOverlay
方法进行移除。其中移除Overlay
有两种操作方式,一种是根据图层移除,另一种是根据图层名称进行移除。根据图层名称进行移除时需要遍历图层,通过图层名属性进行判断移除的目标图层。
scss
/**
* 打开Popup
*/
function openPopup(overlay) {
map.addOverlay(overlay)
}
/**
* 根据图层名关闭Popup
*/
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)
});
}
/**
* 根据图层关闭Popup
*/
function removeOverlayByLayer(overlay) {
map.removeOverlay(overlay)
}
6. 图层点击打开Popup
当在地图上点击到目标要素时,通过获取点击要素的坐标和属性信息,从而打开对应的Popup
。在打开目标Popup
时,需要先将已经打开的Popup
关闭。
arduino
/**
* 根据点击要素打开Popup
*/
function openPopupByClickFeature() {
const popupColumns = [
{ name: 'province', comment: "省" },
{ name: 'region', comment: "行政区" },
{ name: 'population', comment: "人口" }
]
map.on('click', evt => {
const feature = map.forEachFeatureAtPixel(evt.pixel, (feature, layer) => feature)
if (feature) {
// removeOverlayByLayer(overlay)
removeOverlayByName("overLay")
const property = feature.get("properties")
const geometry = feature.getGeometry()
const position = geometry.getCoordinates()
openPopupTable(property, popupColumns, position)
}
})
}
7. 改变鼠标形状
在地图上监听'pointermove'
(鼠标移动)事件,当鼠标移到目标元素时,获取当前屏幕坐标,改变指针形状为"poiter"
arduino
// 当鼠标移过元素时,改变鼠标形状
function changeCursor() {
map.on('pointermove', evt => {
const pixel = map.getEventPixel(evt.originalEvent)
const hit = map.hasFeatureAtPixel(pixel)
map.getTargetElement().style.cursor = hit ? 'poiter' : ''
})
}
8. 封装PopupTable结构
自定义Popup HTML
结构,在表格中展示属性数据。
javascript
/**
* 封装Popup表格内容,显示表格数据
* @properties:要素属性信息,如{province:"四川省",region:"成都市",population:2140.3万人}
* @popupColumns:显示字段对应中文名称:如[{name:province,commnet:"省"},{name:region,comment:行政区},{name:population,comment:"人口"}]
* @position: popup 定位坐标,形式为 [longitude,latitude]
*/
function openPopupTable(property, popupColumns, position) {
const tableEle = document.createElement('table')
const tbodyEle = document.createElement('tbody')
tableEle.appendChild(tbodyEle)
tableEle.className = "popup-table"
tableEle.setAttribute('border', '1')
tableEle.setAttribute('cellpadding', '0')
tableEle.setAttribute('cellspacing', '0')
Object.values(popupColumns).forEach((prop, index) => {
// 过滤无效属性字段
if (prop["name"] === 'id' || prop["name"] === 'oid') return
const trEle = document.createElement('tr')
trEle.className = 'table-tr'
const firstTdEle = document.createElement('td')
const secondTdEle = document.createElement('td')
firstTdEle.innerText = popupColumns[index].comment
secondTdEle.innerText = property[popupColumns[index].name] || '暂无'
trEle.appendChild(firstTdEle)
trEle.appendChild(secondTdEle)
tbodyEle.appendChild(trEle)
})
// 创建Overlay popup
const overlay = new ol.Overlay({
id: "temp-",
position: position,
element: tableEle,
offset: [0, 20], // x、y轴偏移量,正值向右向下,负值相反
autoPan: false,
autoPanMargin: 1.25,
positioning: 'top-center' // 定位方式,顶部居中
})
overlay.setProperties({ layerName: "overLay" })
map.addOverlay(overlay)
}
Popup
样式
css
/**
* openLayers 样式
*/
.ol-overlay-container p {
margin: 5px 0 !important;
}
.ol-overlay-container {
padding: 2px;
max-height: 300px;
color: #fff;
border-radius: 2px;
}
.popup-analyse-btn {
width: 100%;
background-color: rgb(219, 197, 137);
padding: 10px;
border-radius: 2.5px;
margin-top: 10px;
display: inline-block;
color: #fffefe;
border-color: #fff;
text-align: center;
}
.popup-analyse-btn:hover {
cursor: pointer;
color: #fff;
filter: brightness(110%) opacity(100%);
transition: all .5s ease-in;
background: linear-gradient(to bottom right, #9a99f1, #0167cc);
}
.popup-analyse-btn:focus {
filter: brightness(120%);
transition: all .5s ease-in;
background: radial-gradient(circle at center, #9a99f1, #0167cc);
}
.popup-table {
background: #979797ba;
border: 1px solid #d9d9d9ad;
border-collapse: collapse;
}
.table-tr {
width: 100%;
}
.table-tr:hover {
cursor: pointer;
background-color: #0c698d61;
}
.table-tr td {
padding: 10px 5px;
line-height: 1.5;
}
.table-tr td:first-child {
text-align: right;
width: 45%;
}
.table-tr td:last-child {
text-align: left;
width: 55%;
}
9. 完整代码
其中libs
文件夹下的包需要更换为自己下载的本地包或者引用在线资源。
xml
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Popup标注</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">
<link rel="stylesheet" href="./popup.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-text {
font-size: 14px;
font-weight: bold;
color: #434343;
}
.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 xjq = [103.80322263948621, 30.415924063883722]
// 添加图片标注
const imageStyle = new ol.style.Style({
image: new ol.style.Icon({
anchor: [32, 10], // 调整图标位置,正值向左向下,负值相反
anchorOrigin: "bottom-left", // 图标位置起始位置,默认'top-left'
anchorXUnits: 'pixels',
anchorYUnits: 'pixels',
offsetOrigin: 'top-left', // 图标位置偏移起始位置,默认'top-left'
opacity: 0.75,
size: [64, 64], // 图片大小
src: './locate.png'
}),
text: new ol.style.Text({
font: "normal 14px"
})
})
// 添加标注点
const pointLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [
new ol.Feature({
geometry: new ol.geom.Point(chengdu),
name: "chengdu",
properties: {
province: "四川省", region: "成都市", population: "2140.3万人"
}
}),
new ol.Feature({
geometry: new ol.geom.Point(xjq),
name: "xjq",
properties: {
province: "四川省", region: "新津区", population: "36.36万人"
}
})
]
}),
style: imageStyle
})
pointLayer.setProperties({ "layerName": "pointLayer" })
map.addLayer(pointLayer)
// 创建文字内容标注
// const text = "这是四川省"
// const popupEle = createPopupEle(text)
// openPopup(popupEle, chengdu)
// 创建图片内容标注
const mainContent = document.createElement('div')
const p = document.createElement('p')
p.className = 'ol-popup-text'
p.textContent = "成都·历史文化名城市"
const imgEle = document.createElement('img')
imgEle.src = './chengdu.jpg'
imgEle.style.width = '120px'
imgEle.style.height = '50px'
mainContent.appendChild(p)
mainContent.appendChild(imgEle)
const popupEle = createPopupEle(mainContent)
const overlay = createOvrlay(popupEle, chengdu)
openPopup(overlay)
/**
* 打开Popup
*/
function openPopup(overlay) {
map.addOverlay(overlay)
}
/**
* 创建Overlay
* @popupEle:popup html 结构
* @position:popup 定位坐标,形式为 [longitude,latitude]
*/
function createOvrlay(popupEle, position) {
const marker = new ol.Overlay({
id: "maker",
position: position,
element: popupEle,
offset: [0, -30], // x、y轴偏移量,正值向右向下,负值相反
positioning: 'bottom-center', // 定位方式,顶部居中
aotuPan: true,
autoPanMargin: 1.25,
})
marker.setProperties({ layerName: 'overlay' })
return marker
}
/**
* 创建Popup 内容
* @mainContent:HTML 或者 文字
*/
function createPopupEle(mainContent) {
const htmlEle = document.createElement("div")
const closeEle = document.createElement('span')
const mainEle = document.createElement('div')
mainEle.className = 'ol-popup-main'
console.log(typeof mainContent)
const contentType = typeof mainContent
if (contentType === 'object') {
mainEle.appendChild(mainContent)
} else if (contentType === 'string') {
mainEle.innerHTML = mainContent
}
closeEle.textContent = "×"
closeEle.className = "ol-popup-close"
htmlEle.className = "ol-popup"
htmlEle.appendChild(mainEle)
htmlEle.appendChild(closeEle)
// 注册关闭popup事件
closeEle.addEventListener('click', evt => {
removeOverlayByName("overlay")
// removeOverlayByLayer(marker)
})
return htmlEle
}
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)
}
openPopupByClickFeature()
/**
* 根据点击要素打开Popup
*/
function openPopupByClickFeature() {
const popupColumns = [
{ name: 'province', comment: "省" },
{ name: 'region', comment: "行政区" },
{ name: 'population', comment: "人口" }
]
map.on('click', evt => {
const feature = map.forEachFeatureAtPixel(evt.pixel, (feature, layer) => feature)
if (feature) {
// removeOverlayByLayer(overlay)
removeOverlayByName("overLay")
const property = feature.get("properties")
const geometry = feature.getGeometry()
const position = geometry.getCoordinates()
openPopupTable(property, popupColumns, position)
}
})
}
changeCursor()
// 当鼠标移过元素时,改变鼠标形状
function changeCursor() {
map.on('pointermove', evt => {
const pixel = map.getEventPixel(evt.originalEvent)
const hit = map.hasFeatureAtPixel(pixel)
map.getTargetElement().style.cursor = hit ? 'poiter' : ''
})
}
/**
* 封装Popup表格内容,显示表格数据
* @properties:要素属性信息,如{province:"四川省",region:"成都市",population:2140.3万人}
* @popupColumns:显示字段对应中文名称:如[{name:province,commnet:"省"},{name:region,comment:行政区},{name:population,comment:"人口"}]
* @position: popup 定位坐标,形式为 [longitude,latitude]
*/
function openPopupTable(property, popupColumns, position) {
const tableEle = document.createElement('table')
const tbodyEle = document.createElement('tbody')
tableEle.appendChild(tbodyEle)
tableEle.className = "popup-table"
tableEle.setAttribute('border', '1')
tableEle.setAttribute('cellpadding', '0')
tableEle.setAttribute('cellspacing', '0')
Object.values(popupColumns).forEach((prop, index) => {
// 过滤无效属性字段
if (prop["name"] === 'id' || prop["name"] === 'oid') return
const trEle = document.createElement('tr')
trEle.className = 'table-tr'
const firstTdEle = document.createElement('td')
const secondTdEle = document.createElement('td')
firstTdEle.innerText = popupColumns[index].comment
secondTdEle.innerText = property[popupColumns[index].name] || '暂无'
trEle.appendChild(firstTdEle)
trEle.appendChild(secondTdEle)
tbodyEle.appendChild(trEle)
})
// 创建Overlay popup
const overlay = new ol.Overlay({
id: "temp-",
position: position,
element: tableEle,
offset: [0, 20], // x、y轴偏移量,正值向右向下,负值相反
autoPan: false,
autoPanMargin: 1.25,
positioning: 'top-center' // 定位方式,顶部居中
})
overlay.setProperties({ layerName: "overLay" })
map.addOverlay(overlay)
}
</script>
❝
OpenLayers示例数据下载,请回复关键字:ol数据
全国信息化工程师-GIS 应用水平考试资料,请回复关键字:GIS考试
❝【GIS之路】 已经接入了智能助手,欢迎关注,欢迎提问。
欢迎访问我的博客网站-长谈GIS :
http://shanhaitalk.com
都看到这了,不要忘记点赞、收藏 + 关注 哦 !
本号不定时更新有关 GIS开发 相关内容,欢迎关注 !