0 OpenLayers学习笔记-离散知识点

132 阅读9分钟

知识点

  • 获取地图层级 map.getView().getZoom()
  • 获取地图当前屏幕范围map.getView().calculateExtent(map.getSize())
  • 地图的双击事件dblclick
  • 地图移动结束事件moveend
  • 地图的鼠标移动事件pointermove
  • 计算两个坐标之间的距离ol.sphere.getDistance(),单位米,传入的经纬度是WGS84坐标系下的。
  • 用户绘制的圆,获取圆的中心坐标geometry.getCenter()。获取圆的半径geometry.getRadius()
  • 隐藏显示图层layer.setVisible(true/false)
  • 获取图层隐藏/显示状态layer.getVisible()
  • 设置图层层级layer.setZIndex()
  • 获取图层层级layer.getZIndex()
  • 查看版本号ol.util.VERSION
  • 获取地图投影方式map.getView().getProjection().getCode()

代码HTML+CSS+JS

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/ol.min.css" integrity="sha512-bc9nJM5uKHN+wK7rtqMnzlGicwJBWR11SIDFJlYBe5fVOwjHGtXX8KMyYZ4sMgSL0CoUjo4GYgIBucOtqX/RUQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    <title>openlayers离散知识点</title>
    <style>
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    :root {
        --top-height: 50px;
    }
    html,
    body {
        height: 100%;
        height: 100%;
    }
    #container {
        width: 100%;
        height: 100%;
        overflow: hidden;
        position: absolute;
    }
    button {
        border-radius: 0;
        background-color: #07c160;
        border: none;
        outline: none;
        cursor: pointer;
        height: 40px;
        line-height: 40px;
        color: #fff;
        padding: 0 20px;
    }
    select {
        height: 40px;
        line-height: 40px;
        outline: none;
    }
    .ml10 {
        margin-left: 10px;
    }
    #app {
        width: 100vw;
        height: 100vh;
        display: flex;
    }
    .info,
    .app-map {
        height: 100vh;
        width: 50vw;
    }
    .info {
        padding: 30px;
        
    }
    .info > div {
        margin-bottom: 12px;
    }
    .log {
        max-height: 400px;
        overflow: auto;
    }
    .log div {
        height: 30px;
        line-height: 30px;
    }
    .distance,
    .msg {
        height: 40px;
        line-height: 40px;
        font-size: 16px;
    }
    .distance label {
        width: 50px;
        display: inline-block;
    }
    .distance select {
        width: 120px;
        margin-right: 12px;
    }
    </style>
</head>
<body>
    <div id="app">
        <div class="app-map" id="app-map"></div>
        <div class="info">
            <div class='msg'>openlayers 版本号: <b>{{version}}</b></div>
            <div class='msg'>地图展示层级: <b>{{level}}</b></div>
            <div class='msg'>地图投影方式: <b>{{epsg}}</b></div>
            <div class="distance">
                <label for="start">起点</label>
                <select name="start" id="start" v-model='start'>
                    <option v-for='point in pointData' :key='point.name' :value="point.center.join(',')">{{point.name}}</option>
                </select>
                <label for="end">终点</label>
                <select name="end" id="end" v-model='end'>
                    <option v-for='point in pointData' :key='point.name' :value="point.center.join(',')">{{point.name}}</option>
                </select>
                <button @click='handleClickCalcDistance'>计算距离</button>
                <span class="ml10"><b>{{distanceText}}</b></span>
            </div>
            <div>
                <button @click='handleClickDrawCircle'>绘制圆</button>
                <span class="ml10" v-show='center !== ""'>圆的中心坐标: ({{center}})</span>
                <span class="ml10" v-show='radius !== ""'>圆的半径: {{radius}}米</span>
            </div>
            <div>
                <button @click='handleClickTogglePointLayer'>{{this.pointLayerIsVisible ? '隐藏' : '显示'}}点位图层</button>
                <span class="ml10">点位图层状态: {{pointLayerIsVisible ? '显示着' : '隐藏着'}}</span>
            </div>
            <div>
                <button @click='handleClickUpZIndex(2)'>提高点位图层层级</button>
                <button @click='handleClickUpZIndex(1)' class="ml10">提高多边形图层层级</button>
                <span class='index ml10'>点位图层层级:<b>{{pointZIndex}}</b></span>
                <span class='index ml10'>多边形图层层级:<b>{{polygonZIndex}}</b></span>
            </div>
            <div class="log">
                <div v-for='(log, index) in logs' :key='log'>{{index + 1}} {{log}}</div>
            </div>
        </div>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/dist/ol.min.js" integrity="sha512-+nvfloZUX7awRy1yslYBsicmHKh/qFW5w79+AiGiNcbewg0nBy7AS4G3+aK/Rm+eGPOKlO3tLuVphMxFXeKeOQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.14/vue.global.prod.min.js" integrity="sha512-huEQFMCpBzGkSDSPVAeQFMfvWuQJWs09DslYxQ1xHeaCGQlBiky9KKZuXX7zfb0ytmgvfpTIKKAmlCZT94TAlQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script>
    const { createApp } = Vue;
    // feature图片
    const base64Img = 'data:image/png;base64,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';
    // 基础样式style
    const baseStyle = new ol.style.Style({
        image: new ol.style.Icon({
            src: base64Img,
            scale: 1.5,
            anchor: [0.5, 0.5],
            rotateWithView: true,
            rotation: 0,
            opacity: 1
        }),
    });
    // 点位信息
    const pointData = [{
            name: '北京市',
            center: [116.405285, 39.904989]
        },
        {
            name: '天津市',
            center: [117.190182, 39.125596]
        },
        {
            name: '河北省',
            center: [114.502461, 38.045474]
        },
        {
            name: '山西省',
            center: [112.549248, 37.857014]
        },
        {
            name: '内蒙古自治区',
            center: [111.670801, 40.818311]
        },
        {
            name: '辽宁省',
            center: [123.429096, 41.796767]
        },
        {
            name: '吉林省',
            center: [125.3245, 43.886841]
        },
        {
            name: '黑龙江省',
            center: [126.642464, 45.756967]
        },
        {
            name: '上海市',
            center: [121.472644, 31.231706]
        },
        {
            name: '江苏省',
            center: [118.767413, 32.041544]
        },
        {
            name: '浙江省',
            center: [120.153576, 30.287459]
        },
        {
            name: '安徽省',
            center: [117.283042, 31.86119]
        },
        {
            name: '福建省',
            center: [119.306239, 26.075302]
        },
        {
            name: '江西省',
            center: [115.892151, 28.676493]
        },
        {
            name: '山东省',
            center: [117.000923, 36.675807]
        },
        {
            name: '河南省',
            center: [113.665412, 34.757975]
        },
        {
            name: '湖北省',
            center: [114.298572, 30.584355]
        },
        {
            name: '湖南省',
            center: [112.982279, 28.19409]
        },
        {
            name: '广东省',
            center: [113.280637, 23.125178]
        },
        {
            name: '广西壮族自治区',
            center: [108.320004, 22.82402]
        },
        {
            name: '海南省',
            center: [110.33119, 20.031971]
        },
        {
            name: '重庆市',
            center: [106.504962, 29.533155]
        },
        {
            name: '四川省',
            center: [104.065735, 30.659462]
        },
        {
            name: '贵州省',
            center: [106.713478, 26.578343]
        },
        {
            name: '云南省',
            center: [102.712251, 25.040609]
        },
        {
            name: '西藏自治区',
            center: [91.132212, 29.660361]
        },
        {
            name: '陕西省',
            center: [108.948024, 34.263161]
        },
        {
            name: '甘肃省',
            center: [103.823557, 36.058039]
        },
        {
            name: '青海省',
            center: [101.778916, 36.623178]
        },
        {
            name: '宁夏回族自治区',
            center: [106.278179, 38.46637]
        },
        {
            name: '新疆维吾尔自治区',
            center: [87.617733, 43.792818]
        },
        {
            name: '台湾省',
            center: [121.509062, 25.044332]
        },
        {
            name: '香港特别行政区',
            center: [114.173355, 22.320048]
        },
        {
            name: '澳门特别行政区',
            center: [113.54909, 22.198951]
        },
    ]
    function transform (e) {
        return ol.proj.transform(e, 'EPSG:4326', 'EPSG:3857');
    };
    const vm = createApp({
        data() {
            return {
                map: {},
                polygonLayer: {}, // 多边形图层
                pointLayer: {}, // 点位图层
                drawSource: {}, // 绘制图层的数据源
                center: '', // 圆的中心坐标
                radius: '', // 圆的半径
                version: ol.util.VERSION, // openlayers版本号
                epsg: '', // 当前地图的投影方式
                level: 4, // 当前地图层级
                logs: [], // 操作日志
                start: pointData[0].center.join(','), // 距离起始点
                end: pointData[1].center.join(','), // 距离结束点
                distanceText: '', // 计算出来的距离
                pointData, // 点位数据
                draw: null, // 绘制圆的工具
                pointLayerIsVisible: true, // 点位图层显示状态
                pointZIndex: 2, // 点位图层的zindex
                polygonZIndex: 2 // 多边形图层的zindex
            }
        },
        methods: {
            // 初始化地图
            initMap() {
                // 高德地图瓦片地址
                const vectorLayer = new ol.layer.Tile({
                    source: new ol.source.XYZ({
                        url: 'http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}'
                    }),
                    name: '初始化地图图层'
                });
                // 绘制圆形的图层
                this.drawSource = new ol.source.Vector();
                const drawLayer = new ol.layer.Vector({
                    source: this.drawSource,
                });
                //  初始化地图
                this.map = new ol.Map({
                    target: 'app-map',
                    layers: [vectorLayer, drawLayer],
                    view: new ol.View({
                        projection: 'EPSG:3857',
                        //设定中心点,因为默认坐标系为 3587,所以要将我们常用的经纬度坐标系4326 转换为 3587坐标系
                        center: transform([111.8453154, 32.7383500]),
                        zoom: 4,
                    })
                });
                // 绑定地图事件
                this.bindMapEvt();
                // 渲染点位到地图上
                this.renderPoint();
                // 渲染多边形到地图
                this.renderPolygon();
            },
             // 绑定地图事件
            bindMapEvt() {
                // 点击事件
                this.map.on('click', (evt) => {
                    const clickPoint = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326')
                    this.logs.push(`触发了点击事件,点击的经纬度是(${clickPoint[0].toFixed(7)}, ${clickPoint[1].toFixed(7)})`);
                });
                // 双击事件
                this.map.on('dblclick', (evt) => {
                    evt.stopPropagation();
                    const clickPoint = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326')
                    this.logs.push(`触发了双击事件,双击的经纬度是(${clickPoint[0].toFixed(7)}, ${clickPoint[1].toFixed(7)})`);
                });
                // 鼠标移动事件
                this.map.on('pointermove', (e) => {
                    // 获取像素位置
                    let pixel = this.map.getEventPixel(e.originalEvent);
                    // 根据点位像素位置,获取此位置的要素feature
                    let feature = this.map.forEachFeatureAtPixel(pixel, (feature) => {
                        return feature;
                    });
                    // 要素存在,并且是需要改变鼠标样式为pointer的feature,鼠标样式变为pointer,否则auto
                    if (feature && feature.get('pointer')) {
                        console.log(`鼠标移动事件,鼠标移动到了点位上`);
                        this.map.getTargetElement().style.cursor = 'pointer';
                    } else {
                        this.map.getTargetElement().style.cursor = 'auto';
                    }
                });
                // 地图移动结束时间
                this.map.on('moveend', (e) => {
                   const nlevel = this.map.getView().getZoom();
                   if (nlevel === this.level) {
                        this.logs.push(`移动结束事件,根据层级变化,判断为平移 ${nlevel} -> ${nlevel}`);
                   } else {
                        this.logs.push(`移动结束事件,根据层级变化,判断为缩放 ${this.level} -> ${nlevel}`);
                        this.level = nlevel;
                   }
                });
            },
            // 在地图上添加点位
            renderPoint(numPoints) {
                // 根据positions创建一个新的数据源和要素数组,
                const vectorSource = new ol.source.Vector({
                    features: pointData.map(e => {
                        // ol.proj.fromLonLat用于将经纬度坐标从 WGS84 坐标系转换为地图投影坐标系
                        const feature = new ol.Feature({
                            geometry: new ol.geom.Point(ol.proj.fromLonLat(e.center)),
                            pointer: true
                        });
                        feature.setStyle(baseStyle);
                        return feature;
                    })
                });
                // 创建带有数据源的矢量图层
                this.pointLayer = new ol.layer.Vector({
                    source: vectorSource,
                    layerID: 'addpointLayer'
                });
                // 将点位图层添加到地图上
                this.map.addLayer(this.pointLayer);
                this.pointLayer.setZIndex(this.pointZIndex);
                this.pointLayerIsVisible = this.pointLayer.getVisible();
            },
            // 在地图上添加一个多边形图层
            renderPolygon () {
                // 5个点的经纬度坐标
                const coordinates = [
                    [116.405285, 39.904989],
                    [87.617733, 43.792818],
                    [91.132212, 29.660361],
                    [110.33119, 20.031971],
                    [116.405285, 39.904989]
                ];
                const coordinates3857 = coordinates.map(e => {
                    return transform(e);
                });
                // 创建一个多边形要素
                const polygonFeature = new ol.Feature({
                    geometry: new ol.geom.Polygon([coordinates3857]),
                });
                // 设置多边形的样式,填充颜色为绿色
                polygonFeature.setStyle(new ol.style.Style({
                    fill: new ol.style.Fill({
                        color: 'rgba(0, 255, 0, 0.8)' // 绿色填充,透明度为0.5
                    }),
                }));
                // 创建一个矢量图层,并将多边形要素添加到图层中
                this.polygonLayer = new ol.layer.Vector({
                    source: new ol.source.Vector({
                        features: [polygonFeature]
                    })
                });
                this.map.addLayer(this.polygonLayer);
                this.polygonLayer.setZIndex(this.polygonZIndex);
            },
            // 点击计算距离
            handleClickCalcDistance () {
                // baidumap.getDistance(new BMap.Point(116.405285, 39.904989),new BMap.Point(117.190182, 39.125596)) // 109744.28912948008
                const start = (this.start.split(',').map(e => parseFloat(e)));
                const end = (this.end.split(',').map(e => parseFloat(e)));
                this.distanceText = '距离是: ' + ol.sphere.getDistance(start, end) + '米';
                // 创建一条直线
                const lineString = new ol.geom.LineString([transform(start), transform(end)]);
                // 创建Feature
                const lineFeature = new ol.Feature(lineString);
                // 设置Feature样式
                lineFeature.setStyle(new ol.style.Style({
                    stroke: new ol.style.Stroke({
                        color: '#' + Math.random().toString(16).slice(2, 8),
                        width: 3
                    })
                }));
                // 将直线添加到图层
                this.polygonLayer.getSource().addFeature(lineFeature);
            },
            // 点击绘制圆
            handleClickDrawCircle () {
                const { map } = this;
                // 删除上一种绘制
                this.draw && map.removeInteraction(this.draw);
                // 根据 type 创建交互
                this.draw = new ol.interaction.Draw({
                    source: this.drawSource,
                    type: 'Circle'
                });
                // 将交互添加到地图
                map.addInteraction(this.draw);
                // 监听绘制完成的事件
                this.draw.on('drawend', (event) => {
                    // 获取绘制的feature
                    const feature = event.feature;
                    const center = ol.proj.transform(feature.getGeometry().getCenter(), 'EPSG:3857', 'EPSG:4326');
                    const radius = feature.getGeometry().getRadius();
                    this.logs.push(`绘制的圆形的中心经纬度是:(${center[0]}, ${center[1]})`)
                    this.logs.push(`绘制的圆形的半径是:${radius},投影方式是 EPSG:3857,返回的单位是米`);
                    this.center = center.map(e => e.toFixed(6)).join(',')
                    this.radius = radius.toFixed(6);
                    this.map.removeInteraction(this.draw);
                });
            },
            // 切换点位图层的显示隐藏
            handleClickTogglePointLayer () {
                this.pointLayer.setVisible(!this.pointLayerIsVisible);
                this.pointLayerIsVisible = this.pointLayer.getVisible();
            },
            // 提高图层的zindex
            handleClickUpZIndex (type) {
                const layer = type === 1 ? this.polygonLayer : this.pointLayer;
                const text = type === 1 ? '多边形' : '点位';
                let index = layer.getZIndex();
                layer.setZIndex(index + 1);
                type === 1 && (this.polygonZIndex = layer.getZIndex());
                type === 2 && (this.pointZIndex = layer.getZIndex());
                this.logs.push(text + '图层zindex设置为' + (index + 1));
            }
        },
        mounted() {
            this.initMap();
            this.epsg = this.map.getView().getProjection().getCode();
        }
    }).mount('#app')
    </script>
</body>
</html>

参考文章

获取图层显示/隐藏状态方法官方文档

设置图层显示/隐藏状态方法官方文档

获取图层zIndex方式方法官方文档

获取投影方式方法官方文档

设置投影方式方法官方文档

计算点位距离方法官方文档