OpenLayers

9,672 阅读7分钟

一、前言

因为工作需要在使用百度/高德以外实现地图功能。。。。巴拉巴拉。。。。 前文此处省略****字

二、介绍

WebGIS四大地图框架:Leaflet、OpenLayers、Mapbox、Cesium

Leaflet

Leaflet是一个轻量级、易于使用的开源JavaScript库,为开发者提供了创建交互式地图的强大工具。它具有丰富的地图功能和插件生态系统,支持各种地图数据源,从基本的点标记到复杂的地图覆盖物都可以轻松实现。Leaflet的优势在于其简洁性和易用性,使得它成为初学者和专业开发者的首选。

除了在普通地图应用程序中广泛应用外,Leaflet在公共卫生和生物计算等领域也有着广泛的应用。

在公共卫生领域,Leaflet可以用于疫情数据的可视化展示。例如,在新冠疫情期间,Leaflet被广泛应用于疫情数据的可视化展示,以帮助公众更好地了解疫情的传播和分布情况。同时,Leaflet还可以将疫苗接种点的分布数据可视化展示在地图上,以帮助公众查找附近的疫苗接种点和了解疫苗接种进展情况。

OpenLayers

OpenLayers 是一个开源的地图库,用于在 Web 应用中展示交互式地图。它使用 JavaScript 构建,并且支持多种地图数据源,包括 OpenStreetMap、Google Maps、Bing Maps 等。OpenLayers 提供了丰富的地图功能和交互性,使开发者能够在网页中轻松集成地图,并实现各种地理信息展示和交互。

在招聘市场中,OpenLayers的地位也是不可小觑的,能够帮助开发者使网页程序拥有强大的地图功能,用于实施各种项目,包括地理空间搜索、室内分析、地图影像分析和动画模拟等。例如,使用OpenLayers,开发者可以创建支持多个空间图层的互动Web应用程序,可以显示不同底图和支持多种地图操作,比如缩放和平移。此外,开发者还可以在其中添加自定义内容,如文字标签和街景,使Web地图更加完整和生动。

Mapbox

mapbox是一个开源的地图类库,通过使用mapbox的类库,可以很方便的构建web、app等地图应用,支持的地图sdk有web、ios、Android和Unity。

mapbox gl js是mapbox地图的一部分,使用了webgl技术渲染地图上的一些酷炫的效果。

网址:docs.mapbox.com/mapbox-gl-j…

mapbox gl要使用mapbox在线的一些资源,所以,要申请开发者的key才能进行开发使用。

mapbox gl包括了二维三维地图的交互,三维地图是在平面地图的基础上进行立体展示,三维模型是简单的颜色形状渲染,能够基于geojson数据进行渲染,渲染的数据量还是挺可观的,并可以进行数据的交互。

Cesium

Cesium是一个用于创建三维地球和地图应用程序的JavaScript库。它利用WebGL技术实现高性能的地球渲染,为开发者提供了强大的地理空间分析和可视化功能。Cesium不仅支持地球表面的三维渲染,还可以展示地球上的各种数据,如卫星轨道、地质信息等。借助Cesium,开发者可以在Web浏览器中实现惊人的三维地球应用

Cesium目前的功能: 3D地球可视化漫游和导航地形和影像数据3D建筑模型3D TilesCZML实时位置追踪地下和空中场景天文数据集成其他GIS工具

平面:Leaflet,OpenLayers

3D:Mapbox,Cesium

以OpenLayers为例

OpenLayer中的基本对象结构

Map:地图容器,通过target指定地图绘制的容器id,如代码中的map;

Layer:图层,map拥有1个获多个图层,可以理解成PS绘图中的图层概念,整个map是由一个个图层堆叠出来的;

View:可视区域,这个类主要是控制地图与人的交互,如进行缩放,调节分辨率、地图的旋转等控制。同时可以设置投影,默认为球形墨卡托,代码中olProj.fromLonLat方法也是将经纬度转换成对应的坐标,这块不做拓展,有需要的同学可自行去学习GIS的理论。

source:数据来源,即图层Layers的数据组成部分,上文代码中TileLayer就是使用了一个高德地图XYZ格式的切片数据绘制而成,

d7887a57ca31411194ace1c9d22f6e74~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp.png

三、具体实现

本项目基于vue开发

定义瓦片图资源,以天地图为例

typeMapping: {
    img: 'http://t{0-7}.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=',
    cia: 'http://t{0-7}.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=',
    vec: 'http://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=',
    cva: 'http://t{0-7}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk='
},

只要能获取到瓦片图资源即可,也可以使用图片资源,也可以使用百度高德等资源。

地图样式修改

在业务开发中往往需要定制一些特殊样式的地图,在高德和百度地图中往往是可以很方便的进行自定义(毕竟充值了嘛)

在OpenLayers中也可以简单的做一些样式的修改,不过会麻烦一点,可以在view图层中对地图底层的瓦片图图层添加class,通过css滤镜做一些样式上的修改,类似于ps中的图层的一些操作,正片叠底...等等

也可以通过修改瓦片图的图片信息来进行更细致的定制

blog.csdn.net/Aria_Miazzy…

// 最简单的方法
 filter: brightness(115%) grayscale(100%) sepia(62%) hue-rotate(182deg)
    invert(80%) saturate(25%) contrast(1.5);

分层

// 添加地图层
plugins?.forEach((e) => {
    layers.push(
        new TileLayer({
            title: '****图',
            id: `${e}-mapLayer`,
            className: `${e}-mapLayer`,//增加className属性
            source: new XYZ({
                    wrapX: true,
                    url: `${this.typeMapping[e]}${key}`,
            })
        })
    )
})

保存不同的图层便于后期操作

this.markerSource = new VectorSource({ id: 'clusterMarker' });
this.regionSource = new VectorSource({ id: 'region' });

OpenLayers点聚合使用聚合类Cluster

官方文档:openlayers.org/en/v6.15.1/…

使用Cluster类来添加Marker点

try {
    let features = []
    PointData.forEach((e, i) => {
        const feature = new Feature({
            type: 'Point',
            id: e.id || `clusterMarker${i}`,
            geometry: new Point(e.position),
            item: e,
            image: e.content.src,
            style: new VectorStyle({
                image: new Icon(e.content),
                id: e.id || `clusterMarker${i}`,
                geometryName: e.geometryName || `clusterMarker${i}`,
            })
        });
        features.push(feature)
    })
    this.markerSource.addFeatures(features)

    let clusterStrategy = new Cluster({
        distance: parseInt(40, 10),
        minDistance: 20,
    })
    clusterStrategy.setSource(this.markerSource)
    this.geometryLayer.setSource(clusterStrategy)
} catch (err) {
    console.error('点聚合生成错误', err)
}

在 new VectorLayer 中styl实现聚合点不同的动态展示逻辑

在style中使用Map结构记录出现的icon,并记录出现的次数,取聚合点中出现最多的icon进行展示

// 生成标记点图层

layers.push(this.geometryLayer = new VectorLayer({
    id: `geometryLayer`,
    className: `geometryLayer`,
    zIndex: 999,
    source: this.markerSource,
    style: (feature) => {
        let size = feature.get('features')?.length || 1;
        let moreIconMap = new Map
        let moreIcon = { key: '', value: 0 }
        let style
        if (size > 1) {
            feature.get('features').forEach(e => {
                try {
                    let iconPath = ****.png`)
                if (moreIconMap.has(iconPath)) {
                    moreIconMap.set(iconPath, moreIconMap.get(iconPath) + 1)
                } else {
                    moreIconMap.set(iconPath, 1)
                }
                }catch(err){
                    console.error('图片读取错误',err)
                }
            })
            moreIconMap.forEach((value, key) => {
            if (moreIcon.value < value) {
                moreIcon.value = value
                moreIcon.key = key
            }
        })
        
        // 计算出现最多的图标
        style = new VectorStyle({
            image: new Icon({
                src: moreIcon.key,
                scale: 0.8,
            }),
            text: new Text({
                font: 'bold 1rem myFont ',
                offsetY: -39,
                offsetX: 26.9,
                text: size.toString() <= 99?size.toString():'99+',
                fill: new Fill({
                    color: '#fff',
                })
            })
        });
        return style;
        } else {
            return feature.get('features')[0].values_.style
        }
    }
}))

国标2000标准切换

如果需要使用国标2000 需要 使用proj4 自定义添加

projection = "EPSG:4326"

import proj4 from 'proj4';
proj4.defs(
"EPSG:4490",
'GEOGCS["China Geodetic Coordinate System 2000",DATUM["China_2000",SPHEROID["CGCS2000",6378137,298.257222101,AUTHORITY["EPSG","1024"]],AUTHORITY["EPSG","1043"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4490"]]'
);
register(proj4);

信息窗体

vue中使用template的形式编写窗口内容通过ref获取DOM结构,不推荐使用js方式操作原生DOM

在组件内部通过singleclick事件获取鼠标点击位置动态的修改窗口出现位置

image.png


this.windowPoint = new Overlay({
    element: document.getElementById("windowPoint"), //绑定 Overlay 对象和 DOM 对象的
    autoPan: true, // 定义弹出窗口在边缘点击时候可能不完整 设置自动平移效果
    positioning: "bottom-center",
    stopEvent: true,
    offset: [-120, -210],
    autoPanAnimation: {
      duration: 250, //自动平移效果的动画时间 9毫秒
    },
    });
    
this.Omap.addOverlay(this.windowPoint);
      
  // 判断是否点击在点上
let feature = this.Omap.forEachFeatureAtPixel(
  e.pixel,
  (feature) => feature
);

// 设置信息窗口位置
this.windowPoint.setPosition(coordinates);

// 隐藏窗口位置
this.windowPoint.setPosition(void 0);      
        
        
// 设置缩放
this.Omap.getView().animate({
  center: coordinates,
  zoom: this.Omap.getView().getZoom() + 3,
});

// 鼠标变手
 this.Omap.on("pointermove", (e) => {
    if (this.Omap.hasFeatureAtPixel(e.pixel)) {
      this.Omap.getViewport().style.cursor = "pointer";
    } else {
      this.Omap.getViewport().style.cursor = "inherit";
    }
});