注:当前使用的是 ol 5.3.0 版本,天地图使用的
key
请到天地图官网申请,并替换为自己的key
地图标注是将空间位置信息点与地图关联、通过图标、窗口等形式把相关信息展现到地图上。在 我WebGIS
中地图标注是重要的功能之一,可以为用户提供个性化服务,如兴趣点等。地图标注表现方式有图文标注、Popup
标注、聚合标注等。本节主要介绍加载Popup标注。
1. 地图标注基本原理
地图标注通过在已知坐标点添加图片、文字或者图文的方式展现内容信息。可以通过鼠标点击获取目标点位置坐标,也可以通过属性传递获取。
2. 创建图标标注以及Popup加载图片
在图标网站如[https://www.iconfont.cn/](https://www.iconfont.cn/)
下载定位图片,搜索框输入locate
寻找适合的图片。创建图标样式如下,调整图标显示位置anchor
,图标大小size
以及图片路径src
。
// 添加图片标注
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)
3. 创建Popup内容
创建函数createPopupEle
,通过传入HTML
元素或者描述文字生成Popup HTML
。
/*** 创建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 mainContentif (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
,并将其添加到地图中。
/*** 创建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
有两种操作方式,一种是根据图层移除,另一种是根据图层名称进行移除。根据图层名称进行移除时需要遍历图层,通过图层名属性进行判断移除的目标图层。
/*** 打开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
关闭。
/*** 根据点击要素打开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"
// 当鼠标移过元素时,改变鼠标形状
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
结构,在表格中展示属性数据。
/*** 封装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') returnconst trEle = document.createElement('tr')trEle.className = 'table-tr'const firstTdEle = document.createElement('td')const secondTdEle = document.createElement('td')firstTdEle.innerText = popupColumns[index].commentsecondTdEle.innerText = property[popupColumns[index].name] || '暂无'trEle.appendChild(firstTdEle)trEle.appendChild(secondTdEle)tbodyEle.appendChild(trEle)})// 创建Overlay popupconst 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
样式
/*** 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
文件夹下的包需要更换为自己下载的本地包或者引用在线资源。
<!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 mainContentif (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') returnconst trEle = document.createElement('tr')trEle.className = 'table-tr'const firstTdEle = document.createElement('td')const secondTdEle = document.createElement('td')firstTdEle.innerText = popupColumns[index].commentsecondTdEle.innerText = property[popupColumns[index].name] || '暂无'trEle.appendChild(firstTdEle)trEle.appendChild(secondTdEle)tbodyEle.appendChild(trEle)})// 创建Overlay popupconst 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开发 相关内容,欢迎关注 !