OpenLayers 信息提示

89 阅读6分钟

前言

OpenLayers 具有丰富的事件类型,可以满足用户各种个性化的信息显示。在GIS开发中经常需要显示图层或者要素属性信息,而这些信息常常需要用户在进行某项操作或者有需要的时候才进行展示。要实现这样的效果,地图事件是必不可少的,我们可以同通过监听地图点击事件、鼠标移动事件来完成。

本节以云南省GeoJSON数据加载为例,向大家介绍如何结合鼠标事件展示要素属性信息。

如果你还不知道GeoJSON数据在哪下载的话,请参考文章:GeoJSON下载

如果你还想了解更多GeoJSON数据信息,请参考:GeoJSON介绍

1. 创建标注样式

对加载的云南省各州市行政区GeoJSON数据,设置半透明填充和描边样式,然后对标注文本设置填充和描边样式。defaultFillColor为默认填充色,在要素不需要高亮显示后恢复为初始填充色时使用。

// 文字样式
const labelStyle new ol.style.Style({
    textnew ol.style.Text({
        font'12px Calibri,sans-serif',
        overflowtrue,
        // 填充色
        fillnew ol.style.Fill({
            color"#FAFAD2"
        }),
        // 描边色
        strokenew ol.style.Stroke({
            color"#2F4F4F",
            width3,
        })
    })
})
// 行政区样式
const defaultFillColor = [2302302500.25]
const regionStyle new ol.style.Style({
    fillnew ol.style.Fill({
        color: defaultFillColor,
    }),
    strokenew ol.style.Stroke({
        color"#00FFFF",
        width1.25,
    }),
})

在以上代码中分别对行政区图层要素和文字设置样式,通过Style对象管理样式,Fill对象设置填充颜色,Stroke对象设置描边(边线)样式,Text对象用于设置文本样式。

2. 加载行政区图层

通过矢量数据源和矢量图层创建行政区图层,对于GeoJSON数据,使用ol.format.GeoJSON格式进行加载。然后标注文本需要通过样式函数进行设置,要进行标注的文本(字段)可以从参数feature中获取,利用文本标注样式Text对象方法setText设置标注文本,并将declutter属性值设置为true,开启文字压覆模式。

const layer new ol.layer.Vector({
    sourcenew ol.source.Vector({
        url: JSON_URL,
        formatnew ol.format.GeoJSON(),
    }),
    style: function (feature) {
        const label = feature.get("name").split(" ").join("n")
        labelStyle.getText().setText(label)
        return style
    },
    // 开启文字压覆模式
    decluttertrue
})

以下代码将行政区图层数据添加到地图中,为了更好的数据展示效果,重新设置视图中心点和缩放级别。

map.addLayer(layer)
map.getView().setCenter([101.48510625.008643])
map.getView().setZoom(6.5)

3. 信息提示函数

初始化地图查询要素currentFeature和信息提示元素tooltipElementtooltipElement为要素属性信息展示的容器,默认为隐藏状态。

// 当前选择要素
let currentFeature = undefined
// 信息提示元素
const tooltipElement = document.querySelector(".tooltip")

函数表达式displayFeatureInfo需要两个参数,一个地图点击位置坐标,一个点击事件目标元素。在该函数中使用forEachFeatureAtPixel方法获取图层要素,该方法第一个参数为像素坐标,第二个参数为一个函数,函数参数为要素类型,并且在查询到要素时返回要素信息。如果查询到要素信息时,通过lefttop调整信息弹窗距离鼠标点击或者移动位置左边、上边的距离,并且查询要素为新要素时显示信息提示框,填充属性内容。未查询到要素信息则将信息提示框隐藏。最后将新查询要素赋值给当前查询要素。

const displayFeatureInfo = function (pixel, target) {
    const feature = map.forEachFeatureAtPixel(pixel, feature => feature)

    if (feature) {
        tooltipElement.style.left = pixel[0] + 10 + "px"
        tooltipElement.style.top = pixel[1] - 5 + "px"
        if (feature !== currentFeature) {
            tooltipElement.style.visibility = "visible"
            tooltipElement.innerText = feature.get("name")
        }
    } else {
        tooltipElement.style.visibility = "hidden"
    }
    currentFeature = feature
}

4. 监听鼠标事件

在鼠标移动事件pointermove中判断信息提示框是否显示。若鼠标在拖动状态下则不显示提示信息;使用地图对象getEventPixel方法获取浏览器原生事件坐标,并将地图坐标转换为屏幕坐标。

// 监听鼠标移动事件
map.on("pointermove", evt => {
    // 若鼠标处于拖动状态,则不显示信息框,并且返回
    if (evt.dragging) {
        tooltipElement.style.visibility = "hidden"
        currentFeature = undefined
        return
    }
    // 获取事件要素坐标,将地图坐标转换为屏幕坐标
    const pixel = map.getEventPixel(evt.originalEvent)
    // 显示信息框
    displayFeatureInfo(pixel, evt.originalEvent.target)
})

在鼠标点击事件click中显示信息弹框。

// 监听鼠标点击事件
map.on("click", evt => {
    displayFeatureInfo(evt.pixel, evt.originalEvent.target)
})

在鼠标移除事件pointerleave中重置查询要素currentFeature,并隐藏信息提示框。

// 监听鼠标移除事件
map.on("pointerleave", evt => {
    currentFeature = undefined
    // 隐藏信息提示框
    tooltipElement.style.visibility = "hidden"
})

5. 完整代码

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

<!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">
    <script src="../../js/config.js"></script>
    <script src="../../libs/js/ol9.2.4.js"></script>
    <style>
        * {
            padding0;
            margin0;
            font-size14px;
            font-family'微软雅黑';
        }

        html,
        body {
            width100%;
            height100%;
        }

        #map {
            position: absolute;
            top50px;
            bottom0;
            width100%;
        }

        #top-content {
            position: absolute;
            width100%;
            height50px;
            line-height50px;
            backgroundlinear-gradient(135deg#ff00cc#ffcc00#00ffcc#ff0066);
            color#fff;
            text-align: center;
            font-size32px;
        }

        #top-content span {
            font-size32px;
        }

        .tooltip {
            position: absolute;
            z-index100;
            padding5px;
            width: auto;
            height: auto;
            max-width100px;
            max-height50px;
            overflow: auto;
            background-color#66bbaaf8;
            border1.25px solid #eaf515;
            border-radius2.5px;
            color#eaf515;
            visibility: hidden;
            pointer-events: none;
        }
    </style>
</head>

<body>
    <div id="top-content">
        <span>OpenLayers 信息提示</span>
    </div>
    <div id="map" title="">
        <div class="tooltip"></div>
    </div>
</body>

</html>

<script>
    //地图投影坐标系
    const projection = ol.proj.get('EPSG:3857');
    //==============================================================================//
    //============================天地图服务参数简单介绍==============================//
    //================================vec:矢量图层==================================//
    //================================img:影像图层==================================//
    //================================cva:注记图层==================================//
    //======================其中:_c表示经纬度投影,_w表示球面墨卡托投影================//
    //==============================================================================//
    const TDTImgLayer = new ol.layer.Tile({
        title"天地图影像图层",
        sourcenew ol.source.XYZ({
            url"http://t0.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=" + TDTTOKEN,
            attibutions"天地图影像描述",
            crossOrigin"anoymous",
            wrapXfalse
        })
    })
    const TDTImgCvaLayer = new ol.layer.Tile({
        title"天地图影像注记图层",
        sourcenew ol.source.XYZ({
            url"http://t0.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=" + TDTTOKEN,
            attibutions"天地图注记描述",
            crossOrigin"anoymous",
            wrapXfalse
        })
    })
    const map = new ol.Map({
        target"map",
        loadTilesWhileInteractingtrue,
        viewnew ol.View({
            center: [102.84586425.421639],
            zoom6.5,
            worldsWrapfalse,
            minZoom1,
            maxZoom20,
            projection'EPSG:4326',
        }),
        layers: [TDTImgLayer],
        // 地图默认控件
        controls: ol.control.defaults.defaults({
            zoomfalse,
            attributiontrue,
            rotatetrue
        })
    })
    // map.on('click', evt => {
    //     console.log("获取地图坐标:", evt.coordinate)
    // })

    // 文字样式
    const labelStyle = new ol.style.Style({
        textnew ol.style.Text({
            font'12px Calibri,sans-serif',
            overflowtrue,
            // 填充色
            fillnew ol.style.Fill({
                color"#FAFAD2"
            }),
            // 描边色
            strokenew ol.style.Stroke({
                color"#2F4F4F",
                width3,
            }),
        })
    })
    // 行政区样式
    const regionStyle = new ol.style.Style({
        fillnew ol.style.Fill({
            color: [2302302500.25],
        }),
        strokenew ol.style.Stroke({
            color"#00FFFF",
            width1.25,
        }),
    })

    // 标注样式数组
    const style = [labelStyle, regionStyle]

    // 行政区数据
    const JSON_URL = "../../data/geojson/yn_region.json"
    const layer = new ol.layer.Vector({
        sourcenew ol.source.Vector({
            urlJSON_URL,
            formatnew ol.format.GeoJSON(),
        }),
        stylefunction (feature) {
            const label = feature.get("name").split(" ").join("n")
            labelStyle.getText().setText(label)
            return style
        },
        decluttertrue
    })
    map.addLayer(layer)
    map.getView().setCenter([101.48510625.008643])
    map.getView().setZoom(6.5)

    // 当前选择要素
    let currentFeature = undefined
    // 信息提示元素
    const tooltipElement = document.querySelector(".tooltip")
    const displayFeatureInfo = function (pixel, target) {
        const feature = map.forEachFeatureAtPixel(pixel, feature => feature)

        if (feature) {
            tooltipElement.style.left = pixel[0] + 10 + "px"
            tooltipElement.style.top = pixel[1] - 5 + "px"
            if (feature !== currentFeature) {
                tooltipElement.style.visibility = "visible"
                tooltipElement.innerText = feature.get("name")
            }
        } else {
            tooltipElement.style.visibility = "hidden"
        }
        currentFeature = feature
    }
    // 监听鼠标移动事件
    map.on("pointermove"evt => {
        console.log(evt)
        // 若鼠标处于拖动状态,则不显示信息框,并且返回
        if (evt.dragging) {
            tooltipElement.style.visibility = "hidden"
            currentFeature = undefined
            return
        }
        // 获取事件要素坐标,将地图坐标转换为屏幕坐标
        const pixel = map.getEventPixel(evt.originalEvent)
        // 显示信息框
        displayFeatureInfo(pixel, evt.originalEvent.target)
    })

    // 监听鼠标点击事件
    map.on("click"evt => {
        displayFeatureInfo(evt.pixel, evt.originalEvent.target)
    })

    // 监听鼠标移除事件
    map.on("pointerleave"evt => {
        currentFeature = undefined
        // 隐藏信息提示框
        tooltipElement.style.visibility = "hidden"
    })
</script>

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

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

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

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

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

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