OpenLayers 地图标注之聚合标注

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

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

1. 聚合标注基本原理

聚合标注是指在不同地图分辨率下,通过聚合方式展现标注点信息的一种方式。目的是为了减少当前视图下地图标注点的数量,提升客户端渲染速度。如果在地图上添加大量标注点,在地图缩放级别小的情况下则会出现标注点重叠的现象,影响地图表达效果,而且导致地图渲染效率低。

OpenLayers中提供了相应的聚合标注功能,以此提升地图渲染速度,增强用户体验。通过聚合数据源(ol.source.Cluster)实现矢量要素聚合功能。

2. 创建聚合标注

根据左上角和右下角坐标,确定聚合范围,随机创建10000个坐标点,实现图层聚合。通过聚合数据源distance参数设置标注距离,图层样式style函数,可以设置聚合标注样式。

less 复制代码
/**
 * 设置标注点
 * @topLeft:左上角坐标
 * @bottomRight:右下角坐标
 */
const topLeft = [103.45166016463192, 30.841644278727472]
const bottomRight = [104.92822262272239, 30.397796597331766]

// 创建10000个随机点
const features = []
for (let i = 0; i < 10000; i++) {
    // 创建矢量数据源
    const x = topLeft[0] + Math.random() * (bottomRight[0] - topLeft[0])
    const y = bottomRight[1] + Math.random() * (topLeft[1] - bottomRight[1])
    const coordinate = [x, y]
    const feature = new ol.Feature({
        geometry: new ol.geom.Point(coordinate)
    })
    features.push(feature)
}

const pointFeatures = new ol.source.Vector({
    features: features
})
const styleCache = {}

// 创建聚合图层
const pointLayer = new ol.layer.Vector({
    source: new ol.source.Cluster({
        source: pointFeatures, // 聚合数据源
        distance: 40 // 聚合标注距离,当标注间距小于此值时进行聚合
    }),
    style: (feature, resolution) => {
        const size = feature.get('features').length
        let style = styleCache[size]
        if (!style) {
            style = [
                new ol.style.Style({
                    image: new ol.style.Circle({
                        radius: 10,
                        stroke: new ol.style.Stroke({
                            color: 'rgba(255,255,255,1)'
                        }),
                        fill: new ol.style.Fill({
                            color: '#3399CC'
                        })
                    }),
                    text: new ol.style.Text({
                        text: size.toString(),
                        fill: new ol.style.Fill({
                            color: '#fff'
                        })
                    })
                })
            ]
            styleCache[size] = style
        }
        return style
    }
})
pointLayer.setProperties({ "layerName": "pointLayer" })
map.addLayer(pointLayer)

3. 加载和移除聚合图层

通过map.addLayer方法加载聚合图层,map.removeLayer方法移除聚合图层。

dart 复制代码
// 添加聚合图层
document.querySelector('.add-cluster').addEventListener('click', evt => {
    if (pointLayer) {
        map.addLayer(pointLayer)
    }
})

// 移除聚合图层
document.querySelector('.remove-cluster').addEventListener('click', evt => {
    if (pointLayer) {
        map.removeLayer(pointLayer)
    }
})

4. 完整代码

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

xml 复制代码
<!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;
        }

        .cluster-div {
            position: relative;
            padding: 10px 5px;
            left: 50%;
            top: 20px;
            transform: translateX(-50%);
            width: 250px;
            text-align: center;
            background: #2626268a;
            border-radius: 5px;
        }

        .cluster-btn {
            border: none;
            padding: 5px;
            margin: 0 10px;
        }

        .cluster-btn:hover {
            cursor: pointer;
            color: #000;
            font-weight: bold;
            filter: brightness(150%);
        }
    </style>
</head>

<body>
    <div id="map" title="地图显示"></div>
    <div class="cluster-div">
        <button class="cluster-btn add-cluster">添加聚合标注</button>
        <button class="cluster-btn remove-cluster">移除聚合标注</button>
    </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)
    })

    /**
     * 设置标注点
     * @topLeft:左上角坐标
     * @bottomRight:右下角坐标
     */
    const topLeft = [103.45166016463192, 30.841644278727472]
    const bottomRight = [104.92822262272239, 30.397796597331766]

    // 创建10000个随机点
    const features = []
    for (let i = 0; i < 10000; i++) {
        // 创建矢量数据源
        const x = topLeft[0] + Math.random() * (bottomRight[0] - topLeft[0])
        const y = bottomRight[1] + Math.random() * (topLeft[1] - bottomRight[1])
        const coordinate = [x, y]
        const feature = new ol.Feature({
            geometry: new ol.geom.Point(coordinate)
        })
        features.push(feature)
    }

    const pointFeatures = new ol.source.Vector({
        features: features
    })
    const styleCache = {}

    // 创建聚合图层
    const pointLayer = new ol.layer.Vector({
        source: new ol.source.Cluster({
            source: pointFeatures, // 聚合数据源
            distance: 40 // 聚合标注距离,当标注间距小于此值时进行聚合
        }),
        style: (feature, resolution) => {
            const size = feature.get('features').length
            let style = styleCache[size]
            if (!style) {
                style = [
                    new ol.style.Style({
                        image: new ol.style.Circle({
                            radius: 10,
                            stroke: new ol.style.Stroke({
                                color: 'rgba(255,255,255,1)'
                            }),
                            fill: new ol.style.Fill({
                                color: '#3399CC'
                            })
                        }),
                        text: new ol.style.Text({
                            text: size.toString(),
                            fill: new ol.style.Fill({
                                color: '#fff'
                            })
                        })
                    })
                ]
                styleCache[size] = style
            }
            return style
        }
    })
    pointLayer.setProperties({ "layerName": "pointLayer" })
    map.addLayer(pointLayer)

    // 添加聚合图层
    document.querySelector('.add-cluster').addEventListener('click', evt => {
        if (pointLayer) {
            map.addLayer(pointLayer)
        }
    })

    // 移除聚合图层
    document.querySelector('.remove-cluster').addEventListener('click', evt => {
        if (pointLayer) {
            map.removeLayer(pointLayer)
        }
    })

</script>

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

全国信息化工程师-GIS 应用水平考试资料,请回复关键字:GIS考试
【GIS之路】 已经接入了智能助手,欢迎关注,欢迎提问。

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

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

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

相关推荐
棉花糖超人16 分钟前
【从0-1的HTML】第2篇:HTML标签
前端·html
exploration-earth24 分钟前
本地优先的状态管理与工具选型策略
开发语言·前端·javascript
OpenTiny社区40 分钟前
开源之夏报名倒计时3天!还有9个前端任务有余位,快来申请吧~
前端·github
ak啊1 小时前
WebGL魔法:从立方体到逼真阴影的奇妙之旅
前端·webgl
hang_bro1 小时前
使用js方法实现阻止按钮的默认点击事件&触发默认事件
前端·react.js·html
用户90738703648641 小时前
pnpm是如何解决幻影依赖的?
前端
树上有只程序猿1 小时前
Claude 4提升码农生产力的5种高级方式
前端
傻球1 小时前
没想到干前端2年了还能用上高中物理运动学知识
前端·react.js·开源
咚咚咚ddd1 小时前
前端组件:pc端通用新手引导组件最佳实践(React)
前端·react.js
Lazy_zheng1 小时前
🚀 前端开发福音:用 json-server 快速搭建本地 Mock 数据服务
前端·javascript·vue.js