介绍:

OpenLayers官网
OpenLayers插件类型

可以理解为专门处理地图的一个库

其中有两大类 map和view,map是ol中的核心组件,初始化一副地图(map),时,至少需要一个可视化区域(view),一个或多个图层(layer)和一个地图加载的挂载点(target)

1.vue中使用Openlayers

注意:地图容器需要设置宽高,否则看不到初始化的地图效果

地图介绍

一.关于投影

​ 1.投影,地球不是正圆的球体,是一个不规则的椭圆体,所以我们要是想让它展开在桌面上,就会发现地图都会和实际有出入,所以人们发明了各种各样的方式来缩小失真的程度.这种方式就是投用,投影目前常用的是2种,分别是:
​ “EPSG:4326”,在地图上将经纬度直接当做X/Y对待(最常用)
​ "EPSG:3785"又称为球面墨卡托投影。将地图投影到一个地图平面上。为了正确的在商业地图API上叠加地图数据,就必须使用该投影。最基本的是在商业地图API上显示栅格瓦片地图——例如TMS,WMS以及其他类似的瓦片。
​ 2.如果我们不指定特别的投影,OpenLayers的默认投影为EPSG:3857
​ 3.source: 数据源
​ 4.Tile: 瓦片图层类,主要用来加载瓦片图。通过实例化瓦片图层对象,绑定其数据源(source) 加载对应的瓦片地图
​ 5.TileArcGISRest: 使用ArcGis为地底图
​ 6.View: 地图视图类

安装方式:

yarn install ol

2.openlayers简单使用说明

一.初始化地图

import { Map, View } from "ol";//地图,视图
import OSM from "ol/source/OSM"; //可以理解为数据源,就是一张图片
import TileLayer from "ol/layer/Tile"; //可以理解为图层
import { fromLonLat } from "ol/proj";//将坐标从经度/纬度转换为不同的投影。
export default {
  data() {
    return {
      map: null,
    };
  },
  methods: {
    createMap() {
      this.map = new Map({
        target: "map",
        layers: [
          new TileLayer({
            source: new OSM({}),
          }),
        ],
        view: new View({
          center: [116.394926, 39.9125],
          projection: "EPSG:3857",
          zoom: 8,
          maxZoom: 20,
        }),
      });
    },
  },
  mounted() {
    this.createMap();
  },
};
openlayersAPI
ol/proj
import {fromLonLat} from 'ol/proj'; 将坐标从经度/纬度转换为不同的投影。
fromLonLat(coordinat,projection)
coordinat:坐标为经度和纬度,即以经度为第一个元素,纬度为第二个元素的数组。
projection:目标投影。默认为 Web Mercator,即“EPSG:3857”。

import {toLonLat} from 'ol/proj'; 将坐标转换为经度/纬度。
toLonLat(coordinat,projection)
coordinat:投影坐标。
projection:坐标的投影。默认为 Web Mercator,即“EPSG:3857”。

import {transform} from 'ol/proj'; 将坐标从源投影转换为目标投影。这将返回一个新坐标(并且不会修改原始坐标)。
transform(coordinate, source, destination)
coordinate:
source:源投影样。
destination:目的地投影式。

3.openlayers一些配置了解

官网:https://openlayers.org/en/latest/apidoc/

this.map.addLayer(maplayer) //在页面新增图层
this.map.map,removeLayer(maplayer) //删除某一图层可结合this.map.getLayers().array_[index]使用
this.map.getLayers() //获取所有图层信息
maplayer.setOpacity(0); //设置透明度
4.vue使用Openlayers绘制点
//创建空的矢量容器(point)
let vectorSource = new VectorSource({});
      //创建图标层
      let vectorLayer = new VectorLayer({
        source: vectorSource,
      });
      this.clearTc();
      this.map.addLayer(vectorLayer);
      let poi = [];
      if (list.length) {
        list.forEach((item, index) => {
          item.coord = item.jwd;
          // fromLonLat像素点转经纬度
          poi.push(new Feature(new Point(fromLonLat(item.coord))));
          poi[index].set('name', item.name);
          poi[index].set('value', item.value);
          poi[index].set('id', item.id);
          let bdStyle = new Style({
            image: new CircleStyle({
              fill: new Fill({
                color: [128, 0, 128],
              }),
              radius: 6,
            }),
          });
          poi[index].setStyle(bdStyle);
        });
      }
		//根据业务需求所增加的排序
      poi.sort(function (a, b) {
        return b.get('value') - a.get('value');
      });
      poi.forEach((item, index) => {
        vectorSource.addFeature(item);
      });
5.vue使用Openlayers绘制线段
  let newList = [];
    drawList.forEach((item) => {
     newList =item.SectionCoord.geometry.coordinates[0].map(
      (ITEM) => fromLonLat(ITEM););
      let line = new Feature(new LineString(newList)); 
         line.setStyle(
         new Style({
         stroke: new Stroke({
          width: 4,
          color: '#00FF00',
         }),
        })
       );
  let vectorlayer = new VectorLayer({
  		source: new VectorSource({ features: [line] }),
    });
    this.map.addLayer(vectorlayer); //这里是执行,执行之后点就出来了
6.vue使用Openlayers监听点击事件
let that = this;
  this.map.on('click', async (e) => {
  let viewResolution = /** @type {number} */ (that.view.getResolution());
   let url = that.map
          .getLayers()
          .array_[1].getSource()
          .getFeatureInfoUrl(e.coordinate, viewResolution, 'EPSG:3857', {
            INFO_FORMAT: 'application/json',
            FEATURE_COUNT: 50,
          });
if (url) {
  try {
    let res = await this.axios.get(url);
    for (let i = 0; i < res.data.features.length; i++) {
     if (res.data.features[i].id.includes('jgmile')) {
         if (res.data.features[i + 1] == null ||
 			!res.data.features[i].id.includes('jgmile') ) {
             //-----------点击的是区间----------
            let id = res.data.features[i].properties.seg;
            let result = this.mapSectionList.filter((item) => item.SectionId == id)[0];
        this.sectionQjName = `${result.SectionName} 区间 `;
         this.sectionQjNameDisplay = true;
        this.mapSectionInfo = result;
} else if (res.data.features[i + 1].id.includes('jgmile')) {
    //-----------点击的是站点----------
    let name = res.data.features[i].properties.station;
    let result = this.mapStationList.filter((item) => item.StationName == name)[0];
    this.stationFxName = `${result.StationName}`;
    this.stationFxNameDisplay = true;
    this.mapStationInfo = result;
     }
    }
  }
 } catch (e) {
  );
 }
 }
7.vue+openlayers引入高德地图—老版本地图 (网上采取资料实现 – 项目最好不采用)
<template>
  <div class="box">
    <div id="map"></div>
  </div>
</template>
<script>
import { Map, View } from "ol";
import OSM from "ol/source/OSM";
import TileLayer from "ol/layer/Tile";
import XYZ from "ol/source/XYZ";
import { fromLonLat } from "ol/proj";
export default {
  data() {
    return {
      map: null,
    };
  },
  methods: {
    createMap() {
      this.map = new Map({
        target: "map",
        layers: [
          new TileLayer({
            source: new XYZ({
              url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",
            }),
          }),
        ],
        view: new View({
          // center: fromLonLat([116.394926, 39.9125]),
          // center: [0,0],
          center: [116.394926, 39.9125],
          projection: "EPSG:4326",
          zoom: 10,
          maxZoom: 18,
        }),
      });
    },
  },
};
2.同时会出现图片拉伸问题,解决方法:1)引入import { fromLonLat } from "ol/proj";2)将 projection: "EPSG:4326",换成projection: "EPSG:3857"3)然后 center: fromLonLat([116.403414, 39.924091])

ol/proj.fromLonLat (coordinate,opt_projection) //将坐标从经度/纬度转换为不同的投影。
coordinate:[x,y]
8.openlayers引入百度地图 (网上采取资料实现 – 项目最好不采用)
<template >
  <div id="map" style="width: 100vw; height: 100vh"></div>
</template>
<script>
import { Map, View } from "ol";
import Tile from "ol/layer/Tile";
import TileImage from "ol/source/TileImage";
import TileGrid from "ol/tilegrid/TileGrid";
import "ol/ol.css";
import { get } from "ol/proj";
export default {
  data() {
    return {
      map: null,
    };
  },
  methods: {
    createMap() {
      let projection = new get("EPSG:3857");
      let resolutions = [];
      for (let i = 0; i < 19; i++) {
        resolutions[i] = Math.pow(2, 18 - i);
      }
      let tilegrid = new TileGrid({
        origin: [0, 0],
        resolutions: resolutions,
      });
      let baidu_source = new TileImage({
        projection: projection,
        tileGrid: tilegrid,
        tileUrlFunction: function (tileCoord) {
          if (!tileCoord) {
            return "";
          }
          let z = tileCoord[0];
          let x = tileCoord[1];
          let y = -tileCoord[2] - 1;
          if (x < 0) {
            x = "M" + -x;
          }
          if (y < 0) {
            y = "M" + -y;
          }
          return (
            "http://online3.map.bdimg.com/onlinelabel/?qt=tile&x=" +
            x +
            "&y=" +
            y +
            "&z=" +
            z +
            "&styles=pl&udt=20151021&scaler=1&p=1"
          );
        },
      });

      var baidu_layer = new Tile({
        source: baidu_source,
      });
      this.map = new Map({
        target: "map",
        layers: [baidu_layer],
        view: new View({
          center: [12959773, 4853101],
          zoom: 12,
        }),
      });
    },
  },
  mounted() {
    this.createMap();
  },
};
</script>

<style scoped>
.box {
  width: 100% !important;
  height: 100% !important;
  position: relative !important;
}
#container {
  width: 100% !important;
  height: 100% !important;
}
.ol-control {
  display: none !important;
}
</style>
9.Vue+openlayers中使用拖拽缩放控件

代码片段:

import { ZoomSlider } from "ol/control";
createMap() {
      this.map = new Map({
        target: "map",
        layers: [
          new TileLayer({
            source: new XYZ({
              url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",
            }),
          }),
        ],
        view: new View({
          center: fromLonLat([116.394926, 39.9125]),
          projection: "EPSG:3857",
          zoom: 12,
          maxZoom: 20,
        }),
      });
    
      let zoomslider = new ZoomSlider();
      this.map.addControl(zoomslider);
    },
10.openlayers使用全屏控件
import { FullScreen } from "ol/control";
 this.map.addControl(new FullScreen()); //在初始化地图是加上
11.openlayers使用鹰眼控件

注意事项:

  1. 实例化 overviewMapControl 控件,几个常用参数:
    (1) className: 可自定义鹰眼控件的类名
    (2) collapsed: 控件一开始为展开方式
    (3) collapseLabel: 鹰眼展开时功能按钮上的标识
    (4) label: 鹰眼折叠时功能按钮上的标识
  2. 鹰眼的数据源和主地图的数据源要对应一致
  3. 主地图的坐标系和鹰眼的坐标系,必须对应一致。
    如下: 均使用 EPSG:4326 坐标系
import { OverviewMap, defaults as defaultControls } from "ol/control";

createMap() {
      let overviewMapControl = new OverviewMap({
        className: "ol-overviewmap ol-custom-overviewmap",
        layers: [
          new TileLayer({
            source: new XYZ({
              url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",
            }),
          }),
        ],
        view: new View({
          projection: "EPSG:3857", //坐标系
        }),
        collapseLabel: "»", //鹰眼展开时功能按钮上的标识
        label: "«", //鹰眼折叠时功能按钮上的标识
        collapsed: false, //初始为展开显示方式
      });

      this.map = new Map({
        target: "map",
        layers: [
          new TileLayer({
            source: new XYZ({
              url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",
            }),
          }),
        ],
        view: new View({
          center: fromLonLat([116.394926, 39.9125]),
          projection: "EPSG:3857",
          zoom: 12,
          maxZoom: 20,
        }),
      });
      //鹰眼控件
      this.map.addControl(overviewMapControl);
    },
12.点位显示 非聚合
一、实现步骤
  1. 初始化一张地图
  2. 调用 showPoints() 来展示聚合点位,具体如下:
    (1) ol.layer.Vector 创建矢量图层,数据源为 ol.source.Vector 类的实例对象
let vectorLayer = new VectorLayer({
    source: new VectorSource(),
    style: new Style({
        image: new Icon({
            src: pointImg
        })
    })
});

(2) 将点位数据遍历,生成 features 数组
(3) vectorLayer.getSource().addFeatures(features): 将要素添加到 图层数据源中
(4) this.map.addLayer(vectorLayer): 聚合图层添加到地图中

全部代码:

<template >
  <div id="map" style="width: 100vw; height: 100vh"></div>
</template>
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import Tile from "ol/layer/Tile";
import { OSM } from "ol/source";
import { Vector as VectorLayer } from "ol/layer";
import { Icon, Style } from "ol/style";
import VectorSource from "ol/source/Vector";
import Point from "ol/geom/Point";
import Feature from "ol/Feature";
import iconImg from "../../assets/img/device.png"; //图片 可自定义
export default {
  data() {
    return {
      map: null,
    };
  },
  methods: {
    //点位显示
    showPoints() {
      let mapData = [
        {
          domain_id: 10000029,
          domain_name: "北京市第一看守所",
          domain_type_name: "看守所",
          gps_x: "116.40384397",
          gps_y: "39.91487908",
          count: 0,
          channel_id: "",
          group_idx: 1,
        },
        {
          domain_id: 10000028,
          domain_name: "北京市第一看守所",
          domain_type_name: "看守所",
          gps_x: "116.47324397",
          gps_y: "39.91927908",
          count: 0,
          channel_id: "",
          group_idx: 1,
        },
        {
          domain_id: 10000027,
          domain_name: "北京市第一看守所",
          domain_type_name: "看守所",
          gps_x: "116.43324397",
          gps_y: "39.93483908",
          count: 0,
          channel_id: "",
          group_idx: 1,
        },
        {
          domain_id: 10000026,
          domain_name: "北京市第一看守所",
          domain_type_name: "看守所",
          gps_x: "116.50382397",
          gps_y: "39.99487708",
          count: 0,
          channel_id: "",
          group_idx: 1,
        },
      ];
      let vectorLayer = new VectorLayer({
        source: new VectorSource(),
        style: new Style({
          image: new Icon({
            src: iconImg,
          }),
        }),
      });

      let features = [];
      mapData.forEach((item) => {
        if (item.gps_x && item.gps_y) {
          let newObj = Object.assign({}, item);
          newObj.geometry = new Point([Number(item.gps_x), Number(item.gps_y)]);
          features.push(new Feature(newObj));
        }
      });
      vectorLayer.getSource().addFeatures(features);
      this.map.addLayer(vectorLayer);
    },

    //初始化默认地图
    createMap() {
      this.map = new Map({
        target: "map",
        layers: [
          new Tile({
            source: new OSM(),
          }),
        ],
        view: new View({
          center: [116.40387397, 39.91488908],
          zoom: 12,
          maxZoom: 20,
          projection: "EPSG:4326",
        }),
      });
      this.showPoints();
    },
  },
  mounted() {
    this.createMap();
  },
};
</script>

<style scoped>
.box {
  width: 100% !important;
  height: 100% !important;
  position: relative !important;
}
#container {
  width: 100% !important;
  height: 100% !important;
}
.ol-control {
  display: none !important;
}
</style>
13.点位显示 聚合
一、实现步骤
  1. 初始化一张地图
  2. 调用 showPoints() 来展示聚合点位,具体如下:
    (1) ol.source.Cluster 创建聚合对象并设置数据源和距离
    (2) ol.layer.Vector 创建矢量图层,并为图层设置聚合数据源
    (3) 将点位数据遍历,生成 features 数组
    (4) sourceCluster.getSource().addFeatures(features): 将要素添加到数据源中
    (5) this.map.addLayer(vectorLayer): 聚合图层添加到地图中
二、注意实现

1、创建矢量图层 vectorLayer 时候,可进行图层样式设置。
2、图层样式,分聚合和非聚合两种状态,调用 setClusterStyle 方法设置

const vectorLayer = new VectorLayer({
    source: sourceCluster,
    style: feature => this.setClusterStyle(feature)
});
<template>
<div class="map" id="map"></div>
</template>
<script>
    import "ol/ol.css";
    import Map from "ol/Map";
    import View from "ol/View";
    import { Cluster, Vector as VectorSource, OSM } from "ol/source";
    import {
        Circle as CircleStyle,
        Fill,
        Stroke,
        Style,
        Text,
        Icon,
    } from "ol/style";
    import { Vector as VectorLayer } from "ol/layer";
    import TileLayer from "ol/layer/Tile";
    import Point from "ol/geom/Point";
    import Feature from "ol/Feature";

    import pointImg from "../../assets/img/device.png";

    export default {
        name: "clusterLayout",

        data() {
            return {
                map: null,
            };
        },

        methods: {
            //初始化地图
            init() {
                this.map = new Map({
                    target: "map",
                    layers: [
                        new TileLayer({
                            source: new OSM({}),
                        }),
                    ],
                    view: new View({
                        center: [116.40387397, 39.91488908],
                        maxZoom: 18,
                        minZoom: 8,
                        zoom: 13,
                        projection: "EPSG:4326",
                    }),
                });
                this.showPoints();
            },

            //设置聚合图层的样式
            setClusterStyle(feature) {
                let features = feature.get("features");
                let size = features.length;
                let style;
                if (size == 1) {
                    style = [
                        new Style({
                            image: new Icon({
                                src: pointImg,
                            }),
                        }),
                    ];
                } else {
                    style = new Style({
                        image: new CircleStyle({
                            radius: 18,
                            stroke: new Stroke({
                                color: "#fff",
                            }),
                            fill: new Fill({
                                color: "#3399CC",
                            }),
                        }),
                        text: new Text({
                            font: "15px sans-serif",
                            text: size.toString(),
                            fill: new Fill({
                                color: "#fff",
                            }),
                        }),
                    });
                }
                return style;
            },

            // 展示聚合点位
            showPoints() {
                let mapData = [
                    {
                        domain_id: 10000029,
                        domain_name: "北京市第一看守所",
                        domain_type_name: "看守所",
                        gps_x: "116.40384397",
                        gps_y: "39.91487908",
                        count: 0,
                        channel_id: "",
                        group_idx: 1,
                    },
                    {
                        domain_id: 10000028,
                        domain_name: "北京市第一看守所",
                        domain_type_name: "看守所",
                        gps_x: "116.47324397",
                        gps_y: "39.91927908",
                        count: 0,
                        channel_id: "",
                        group_idx: 1,
                    },
                    {
                        domain_id: 10000027,
                        domain_name: "北京市第一看守所",
                        domain_type_name: "看守所",
                        gps_x: "116.43324397",
                        gps_y: "39.93483908",
                        count: 0,
                        channel_id: "",
                        group_idx: 1,
                    },
                    {
                        domain_id: 10000026,
                        domain_name: "北京市第一看守所",
                        domain_type_name: "看守所",
                        gps_x: "116.50382397",
                        gps_y: "39.99487708",
                        count: 0,
                        channel_id: "",
                        group_idx: 1,
                    },
                ];

                // 聚合图层数据源
                let sourceCluster = new Cluster({
                    distance: 100,
                    source: new VectorSource({
                        features: [],
                    }),
                });

                let vectorLayer = new VectorLayer({
                    source: sourceCluster,
                    style: (feature) => this.setClusterStyle(feature),
                });

                let features = [];
                mapData.forEach((item) => {
                    if (item.gps_x != "" && item.gps_y != "") {
                        let newObj = Object.assign({}, item);
                        newObj.geometry = new Point([Number(item.gps_x), Number(item.gps_y)]);
                        features.push(new Feature(newObj));
                    }
                });
                sourceCluster.getSource().addFeatures(features);
                this.map.addLayer(vectorLayer);
            },
        },

        mounted() {
            this.init();
        },
    };
</script>

<style scoped>
    .map {
        width: 100vw;
        height: 100vh;
    }
</style>
14.鼠标移入或点击显示气泡
一、实现关键
  1. addPopup(): 地图初始化后,添加 overlay 到地图中
  2. map对象添加 click 事件监听
this.map.on("click", evt => {
    this.mapPointerClick(evt);
});
  1. mapPointerClick(): 鼠标移动的回调函数,判断鼠标移动之处的feature是否只有单个点位图层。只有单个点位图层,则弹出气泡显示详情
  2. 要素feature的数据,使用get()方法来获取
feature.get("domain_type_name")
二、注意事项

对于png图片,透明部分点击触发不了地图事件

<template>
<div>
    <div class="map" id="map"></div>
    <div id="popup" class="ol-popup">
        <a href="#" id="popup-closer" class="ol-popup-closer"></a>
        <div id="popup-content"></div>
    </div>
    </div>
</template>
<script>
    import "ol/ol.css";
    import { Map, View } from "ol";
    import { Cluster, Vector as VectorSource, OSM } from "ol/source";
    import {
        Circle as CircleStyle,
        Fill,
        Stroke,
        Style,
        Text,
        Icon,
    } from "ol/style";
    import { Vector as VectorLayer } from "ol/layer";
    import TileLayer from "ol/layer/Tile";
    import Point from "ol/geom/Point";
    import Feature from "ol/Feature";
    import Overlay from "ol/Overlay";
    import pointImg from "../../assets/img/device.png";
    export default {
        data() {
            return {
                map: null,
                sourceCluster: null, //聚合层数据源
                overlay: null, //弹出层数据源
            };
        },

        methods: {
            //初始化地图
            init() {
                this.map = new Map({
                    target: "map",
                    layers: [
                        new TileLayer({
                            source: new OSM({}),
                        }),
                    ],
                    view: new View({
                        center: [116.40387397, 39.91488908],
                        maxZoom: 18,
                        minZoom: 8,
                        zoom: 13,
                        projection: "EPSG:4326",
                    }),
                });
                this.showPoints(); // 展示聚合点位
                this.addPopup(); //增加弹出层
                //map对象添加 pointermove 事件监听
                this.map.on("click", (evt) => {
                    this.mapPointerMove(evt);
                });
            },

            // 展示聚合点位
            showPoints() {
                let mapData = [
                    {
                        domain_id: 10000029,
                        domain_name: "北京市第一看守所",
                        domain_type_name: "看守所",
                        gps_x: "116.40384397",
                        gps_y: "39.91487908",
                        count: 0,
                        channel_id: "",
                        group_idx: 1,
                    },
                    {
                        domain_id: 10000028,
                        domain_name: "北京市第二看守所",
                        domain_type_name: "看守所",
                        gps_x: "116.47324397",
                        gps_y: "39.91927908",
                        count: 0,
                        channel_id: "",
                        group_idx: 1,
                    },
                    {
                        domain_id: 10000027,
                        domain_name: "北京市第三看守所",
                        domain_type_name: "看守所",
                        gps_x: "116.43324397",
                        gps_y: "39.93483908",
                        count: 0,
                        channel_id: "",
                        group_idx: 1,
                    },
                    {
                        domain_id: 10000026,
                        domain_name: "北京市第四看守所",
                        domain_type_name: "看守所",
                        gps_x: "116.50382397",
                        gps_y: "39.99487708",
                        count: 0,
                        channel_id: "",
                        group_idx: 1,
                    },
                ];

                // 聚合图层数据源
                this.sourceCluster = new Cluster({
                    distance: 100,
                    source: new VectorSource({
                        features: [],
                    }),
                });

                let vectorLayer = new VectorLayer({
                    source: this.sourceCluster,
                    style: (feature) => this.setClusterStyle(feature),
                });

                let features = [];
                mapData.forEach((item) => {
                    if (item.gps_x != "" && item.gps_y != "") {
                        let newObj = Object.assign({}, item);
                        newObj.geometry = new Point([Number(item.gps_x), Number(item.gps_y)]);
                        features.push(new Feature(newObj));
                    }
                });
                this.sourceCluster.getSource().addFeatures(features);
                this.map.addLayer(vectorLayer);
            },

            //设置聚合图层的样式
            setClusterStyle(feature) {
                let features = feature.get("features");
                let size = features.length;
                let style;
                if (size == 1) {
                    style = [
                        new Style({
                            image: new Icon({
                                src: pointImg,
                            }),
                        }),
                    ];
                } else {
                    style = new Style({
                        image: new CircleStyle({
                            radius: 18,
                            stroke: new Stroke({
                                color: "#fff",
                            }),
                            fill: new Fill({
                                color: "#3399CC",
                            }),
                        }),
                        text: new Text({
                            font: "15px sans-serif",
                            text: size.toString(),
                            fill: new Fill({
                                color: "#fff",
                            }),
                        }),
                    });
                }
                return style;
            },

            //添加弹出层
            addPopup() {
                this.overlay = new Overlay({
                    element: document.getElementById("popup"),
                    offset: [0, -13],
                });
                //添加 overlay 到 map
                this.map.addOverlay(this.overlay);

                //关闭弹出层
                let closer = document.getElementById("popup-closer");
                closer.onclick = () => {
                    this.overlay.setPosition(undefined);
                    closer.blur();
                    return false;
                };
            },

            //鼠标移动事件
            mapPointerMove(evt) {
                let coord = []; //坐标数组
                // this.map.getEventPixel() // 返回浏览器事件相对于视口的地图像素位置。
                let pixel = this.map.getEventPixel(evt.originalEvent);
                // forEachFeatureAtPixel(像素,回调,opt_options) //检测与视口上的像素相交的特征,并对每个相交的特征执行回调。检测中包含的层可以通过 中的layerFilter选项进行配置opt_options
                let featureMouseOver = this.map.forEachFeatureAtPixel(
                    pixel,
                    (feature) => feature
                );
                // featureMouseOver.getProperties() 获取所有属性名称和值的对象
                if (featureMouseOver) {
                    if (featureMouseOver.getProperties().features) {
                        //聚合情况下
                        if (featureMouseOver.getProperties().features.length == 1) {
                            //只有当前没有聚合才可以显示
                            let onceElement = featureMouseOver.getProperties().features[0];
                            // this.sourceCluster.getSource() 获取图层源
                            // this.sourceCluster.getSource().forEachFeature(callback) 遍历源上的所有功能,对每个功能调用提供的回调。如果回调返回任何“真实”值,迭代将停止并且函数将返回相同的值。注意:此函数仅迭代具有定义几何的特征。
                            this.sourceCluster.getSource().forEachFeature((feature) => {
                                if (onceElement == feature) {
                                    coord[0] = Number(feature.get("gps_x"));
                                    coord[1] = Number(feature.get("gps_y"));
                                    let name = feature.get("domain_name");
                                    let siteType = feature.get("domain_type_name");
                                    document.getElementById(
                                        "popup-content"
                                    ).innerHTML = `<p style="font-size:15px;">当前所在地名称: ${name}</p><p style="font-size:15px;">所在地类型: ${siteType}</p>`;
                                    //  this.overlay.setPosition(坐标) 设置此叠加层的位置。如果位置undefined是隐藏的覆盖。
                                    this.overlay.setPosition(coord);
                                }
                            });
                        }
                    }
                }

                //如果左边数组没有值的话就添加个undeifend
                if (!coord.length) {
                    this.overlay.setPosition(undefined);
                }
            },
        },
        mounted() {
            this.init();
        },
    };
</script>

<style scoped>
    .map {
        width: 100vw;
        height: 100vh;
    }
    .ol-popup {
        position: absolute;
        background-color: white;
        box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
        padding: 15px;
        border-radius: 10px;
        border: 1px solid #cccccc;
        bottom: 12px;
        left: -50px;
        min-width: 280px;
    }
    .ol-popup:after,
    .ol-popup:before {
        top: 100%;
        border: solid transparent;
        content: " ";
        height: 0;
        width: 0;
        position: absolute;
        pointer-events: none;
    }
    .ol-popup:after {
        border-top-color: white;
        border-width: 10px;
        left: 48px;
        margin-left: -10px;
    }
    .ol-popup:before {
        border-top-color: #cccccc;
        border-width: 11px;
        left: 48px;
        margin-left: -11px;
    }
    .ol-popup-closer {
        text-decoration: none;
        position: absolute;
        top: 2px;
        right: 8px;
    }
    .ol-popup-closer:after {
        content: "✖";
    }
</style>

15.绘制 点 线 圆 多边形

如果需要修改绘制的类型 就更换Draw下type的值
点:Point 线:LineString 圆:Circle 多边形:Polygon

注意:如果把4中类型的绘制集成起来 必须要写删除交互 否则回不断的记录你上次选择的type 就会造成你绘制圆的同时也会绘制其他的形状

<template >
<div style="width: 100vw; height: 100vh">
    <div id="map" style="width: 100%; height: 100%"></div>
    <div class="btn">
        <el-button @click="drawPoint('点')">点</el-button>
        <el-button @click="drawPoint('线')">线</el-button>
        <el-button @click="drawPoint('圆')">圆</el-button>
        <el-button @click="drawPoint('多边形')">多边形</el-button>
        <el-button @click="clearDraw">清除绘画</el-button>
    </div>
    </div>
</template>
<script>
    import "ol/ol.css";
    import { Map, View } from "ol";
    import Tile from "ol/layer/Tile";
    import { OSM } from "ol/source";
    import { fromLonLat } from "ol/proj";
    import VectorLayer from "ol/layer/Vector"; //新的矢量层
    import VectorSource from "ol/source/Vector"; //新的矢量源
    import { Fill, Style, Stroke, Circle as CircleStyle } from "ol/style";
    import { Draw } from "ol/interaction";

    export default {
        data() {
            return {
                map: null, //地图层
            };
        },
        methods: {
            //初始化默认地图
            createMap() {
                this.map = new Map({
                    target: "map",
                    layers: [
                        new Tile({
                            source: new OSM(),
                        }),
                        this.initDrawLayers(),
                    ],
                    view: new View({
                        center: fromLonLat([116.40387397, 39.91488908]),
                        zoom: 12,
                        maxZoom: 20,
                        projection: "EPSG:3857",
                    }),
                });
            },

            // 增加点
            initDrawLayers() {
                let drawLayers = new VectorLayer({
                    source: new VectorSource(),
                    style: new Style({
                        //填充 设置矢量特征的填充样式
                        fill: new Fill({
                            color: "rgba(255, 255, 255, 0.2)",
                        }),

                        //画   线条时的线条的颜色
                        stroke: new Stroke({
                            color: "blue",
                            width: 2,
                        }),

                        //绘制点的颜色
                        image: new CircleStyle({
                            radius: 7,
                            fill: new Fill({
                                color: "green",
                            }),
                        }),
                    }),
                });
                return drawLayers;
            },

            //绘制
            drawPoint(drawType) {
                let type = "";
                switch (drawType) {
                    case "点":
                        type = "Point";
                        break;
                    case "线":
                        type = "LineString";
                        break;
                    case "圆":
                        type = "Circle";
                        break;
                    case "多边形":
                        type = "Polygon";
                        break;
                    default:
                        break;
                }
                
                if (this.draw) {
                    this.map.removeInteraction(this.draw);   //removeInteraction 删除交互
                }  
                this.draw = new Draw({
                    source: this.map.getLayers().array_[1].getSource(),
                    type,
                });
                this.map.addInteraction(this.draw);  // this.map.addInteraction 设置交互
            },

            //清除会话
            clearDraw() {
                //采取链式操作 减少多余代码显示
                this.map.getLayers().array_[1].getSource().clear();
            },
            
        },
        mounted() {
            this.createMap();
        },
        
    };
</script>

<style scoped>
    .btn {
        position: fixed;
        top: 10px;
        right: 10px;
    }
</style>
16.测量距离
一、注意实现
  1. ol/sphere里有getLength()和getArea()可以用来测量距离和区域面积,默认的投影坐标系是EPSG:3857
  2. 若是 EPSG:3857, 需要转换,直接使用 getLength() 和 getArea()
  3. 若不是EPSG:3857,则方案有两:
    (1)使用时, 可以先把几何图形转换成‘EPSG:3857 ’的投影方式,然后再使用getLength和getArea
    (2) ol/sphere里有getLength()和getArea(),可以设置options参数
var formatLength = function (line) {
	var sourceProj = map.getView().getProjection();//获取投影坐标系
	/****** 方法1 *********/
	// var geom = (line.clone().transform(sourceProj, 'EPSG:3857'));
	// var length = getLength(geom);

	/****** 方法2 *********/
	var length = getLength(line, {projection: sourceProj/*, radius: 6371008.8*/});
    ...
};

代码展示:

<template >
  <div style="width: 100vw; height: 100vh">
    <div id="map" style="width: 100%; height: 100%"></div>
  </div>
</template>
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import Tile from "ol/layer/Tile";
import { OSM, Vector as VectorSource } from "ol/source";
import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style";
import { Vector as VectorLayer } from "ol/layer";
import { fromLonLat } from "ol/proj";
import Draw from "ol/interaction/Draw";
import Overlay from "ol/Overlay";
import { LineString } from "ol/geom";
import { getLength } from "ol/sphere";
import { unByKey } from "ol/Observable";

export default {
  data() {
    return {
      map: null,
      // vector: null,
      helpTooltipElement: null,
      measureTooltip: null,
      measureTooltipElement: null,
      raster: null,
      vector: null,
      sketch: null, //Currently drawn feature
      helpTooltip: null, //Overlay to show the help messages.
      draw: null, //global so we can remove it later
      listener: null,
    };
  },
  methods: {
    //初始化默认地图
    createMap() {
      let vector = new VectorLayer({
        source: new VectorSource(),
        style: new Style({
          stroke: new Stroke({
            color: "green",
            width: 2,
          }),
        }),
      });

      this.map = new Map({
        target: "map",
        layers: [
          new Tile({
            source: new OSM(),
          }),
          vector,
        ],
        view: new View({
          center: fromLonLat([116.40387397, 39.91488908]),
          zoom: 12,
          maxZoom: 25,
          projection: "EPSG:3857",
        }),
      });
      this.addInteraction();
    },

    //创建鼠标指针
    addInteraction() {
      this.draw = new Draw({
        source: this.map.getLayers().array_[1].getSource(),
        type: "LineString",
        style: new Style({
          stroke: new Stroke({
            color: "rgba(0, 0, 0, 0.5)",
            lineDash: [10, 10],
            width: 2,
          }),
          image: new CircleStyle({
            radius: 5,
            stroke: new Stroke({
              color: "rgba(0, 0, 0, 0.7)",
            }),
            fill: new Fill({
              color: "rgba(255, 255, 255, 0.2)",
            }),
          }),
        }),
      });

      this.map.addInteraction(this.draw);
      this.createMeasureTooltip();
      this.drawHandler();
    },

    //创建测量工具
    createMeasureTooltip() {
      if (this.measureTooltipElement) {
        this.measureTooltipElement.parentNode.removeChild(
          this.measureTooltipElement
        );
      }
      this.measureTooltipElement = document.createElement("div");
      this.measureTooltipElement.className = "ol-tooltip ol-tooltip-measure";
      this.measureTooltip = new Overlay({
        element: this.measureTooltipElement,
        offset: [0, -15],
        positioning: "bottom-center",
      });
      this.map.addOverlay(this.measureTooltip);
    },

    drawHandler() {
      this.draw.on("drawstart", (evt) => {
        this.sketch = evt.feature;
        let tooltipCoord = evt.coordinate;
        this.listener = this.sketch.getGeometry().on("change", (evt) => {
          let output;
          let geom = evt.target;
          if (geom instanceof LineString) {
            output = this.formatLength(geom);
            tooltipCoord = geom.getLastCoordinate();
          }
          this.measureTooltipElement.innerHTML = output;
          this.measureTooltip.setPosition(tooltipCoord);
        });
      });

      this.draw.on("drawend", () => {
        this.measureTooltipElement.className = "ol-tooltip ol-tooltip-static";
        this.measureTooltip.setOffset([0, -7]);
        this.sketch = null;
        this.measureTooltipElement = null;
        this.createMeasureTooltip();
        unByKey(this.listener);
      });
    },

    formatLength(line) {
      let sourceProj = this.map.getView().getProjection(); //获取投影坐标系
      let length = getLength(line, { projection: sourceProj });
      let output;
      if (length > 100) {
        output = Math.round((length / 1000) * 100) / 100 + " " + "km";
      } else {
        output = Math.round(length * 100) / 100 + " " + "m";
      }
      return output;
    },
  },
  mounted() {
    this.createMap();
  },
};
</script>

<style scoped lang="less">
// 提示的样式
/deep/ .ol-tooltip {
  position: relative;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 4px;
  color: white;
  padding: 4px 8px;
  opacity: 0.7;
  white-space: nowrap;
  font-size: 12px;
}
//移动时的样式
/deep/ .ol-tooltip-measure {
  opacity: 1;
  font-weight: bold;
}
//双击时的样式
/deep/ .ol-tooltip-static {
  background-color: #ffcc33;
  color: black;
  border: 1px solid white;
}
//下三角
/deep/ .ol-tooltip-measure:before,
/deep/ .ol-tooltip-static:before {
  border-top: 6px solid rgba(0, 0, 0, 0.5);
  border-right: 6px solid transparent;
  border-left: 6px solid transparent;
  content: "";
  position: absolute;
  bottom: -6px;
  margin-left: -7px;
  left: 50%;
}
/deep/ .ol-tooltip-static:before {
  border-top-color: #ffcc33;
}
</style>
17.测面
一、注意实现
  1. ol/sphere里有getLength()和getArea()可以用来测量距离和区域面积,默认的投影坐标系是EPSG:3857
  2. 若是 EPSG:3857, 需要转换,直接使用 getLength() 和 getArea()
  3. 若不是EPSG:3857,则方案有两:
    (1)使用时, 可以先把几何图形转换成‘EPSG:3857 ’的投影方式,然后再使用getLength和getArea
    (2) ol/sphere里有getLength()和getArea(),可以设置options参数
var formatArea = function(polygon){
	var sourceProj = map.getView().getProjection();//获取投影坐标系
	/****** 方法1 *********/
	// var geom = (polygon.clone().transform(sourceProj, 'EPSG:3857'));
	// var area = getArea(geom);
	/****** 方法2 *********/
	var area = getArea(polygon, {projection: sourceProj/*, radius: 6371008.8*/})
};
<template>
  <div class="map" id="map"></div>
</template>

<script>
import "ol/ol.css";
import Draw from "ol/interaction/Draw";
import { Map, View } from "ol";
import Overlay from "ol/Overlay";
import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style";
import { Polygon } from "ol/geom";
import { OSM, Vector as VectorSource } from "ol/source";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import { getArea } from "ol/sphere";
import { unByKey } from "ol/Observable";
import { fromLonLat } from "ol/proj";
export default {
  data() {
    return {
      map: null,
      vector: null,
      sketch: null, //当前绘制的样式
      measureTooltipElement: null, //测量工具提示元素
      measureTooltip: null, //叠加显示测量值
      draw: null, //把用来绘画的图层全局化 方便我们删除
      listener: null,
    };
  },

  methods: {
    init() {
      this.vector = new VectorLayer({
        source: new VectorSource(),
        style: new Style({
          fill: new Fill({
            color: "rgba(255, 255, 255, 0.5)",
          }),
          stroke: new Stroke({
            color: "#ffcc33",
            width: 2,
          }),
          image: new CircleStyle({
            radius: 7,
            fill: new Fill({
              color: "#ffcc33",
            }),
          }),
        }),
      });

      //初始化地图
      this.map = new Map({
        target: "map",
        layers: [
          new TileLayer({
            source: new OSM({}),
          }),
          this.vector,
        ],
        view: new View({
          center: fromLonLat([116.40387397, 39.91488908]),
          maxZoom: 18,
          zoom: 12,
          projection: "EPSG:3857",
        }),
      });

      // 添加map事件
      this.addInteraction();
    },

    addInteraction() {
      this.draw = new Draw({
        source: this.vector.getSource(),
        type: "Polygon",
        style: new Style({
          fill: new Fill({
            color: "rgba(255, 255, 255, 0.5)",
          }),

          stroke: new Stroke({
            color: "rgba(0, 0, 0, 0.5)",
            lineDash: [10, 10],
            width: 2,
          }),

          image: new CircleStyle({
            radius: 5,
            stroke: new Stroke({
              color: "rgba(0, 0, 0, 0.7)",
            }),
            fill: new Fill({
              color: "rgba(255, 255, 255, 0.2)",
            }),
          }),
        }),
      });

      this.map.addInteraction(this.draw);
      this.createMeasureTooltip();
      this.drawHandler();
    },

    formatArea(polygon) {
      let sourceProj = this.map.getView().getProjection(); //获取投影坐标系
      let geom = polygon.clone().transform(sourceProj, "EPSG:3857");
      let area = getArea(geom);
      let output;
      if (area > 10000) {
        output =
          Math.round((area / 1000000) * 100) / 100 + " " + "km<sup>2</sup>";
      } else {
        output = Math.round(area * 100) / 100 + " " + "m<sup>2</sup>";
      }
      return output;
    },

    createMeasureTooltip() {
      if (this.measureTooltipElement) {
        this.measureTooltipElement.parentNode.removeChild(
          this.measureTooltipElement
        );
      }
      this.measureTooltipElement = document.createElement("div");
      this.measureTooltipElement.className = "ol-tooltip ol-tooltip-measure";
      this.measureTooltip = new Overlay({
        element: this.measureTooltipElement,
        offset: [0, -15],
        positioning: "bottom-center",
      });
      this.map.addOverlay(this.measureTooltip);
    },

    drawHandler() {
      this.draw.on("drawstart", (evt) => {
        this.sketch = evt.feature;
        let tooltipCoord = evt.coordinate;

        this.listener = this.sketch.getGeometry().on("change", (evt) => {
          let output;
          let geom = evt.target;
          if (geom instanceof Polygon) {
            output = this.formatArea(geom);
            tooltipCoord = geom.getLastCoordinate();
          }
          this.measureTooltipElement.innerHTML = output;
          this.measureTooltip.setPosition(tooltipCoord);
        });
      });

      this.draw.on("drawend", () => {
        this.measureTooltipElement.className = "ol-tooltip ol-tooltip-static";
        this.measureTooltip.setOffset([0, -7]);
        this.sketch = null;
        this.measureTooltipElement = null;
        this.createMeasureTooltip();
        unByKey(this.listener);
      });
    },
  },

  mounted() {
    this.init();
  },
};
</script>

<style lang="less" scoped>
.map {
  width: 100vw;
  height: 100vh;
  /deep/ .ol-tooltip {
    position: relative;
    background: rgba(0, 0, 0, 0.5);
    border-radius: 4px;
    color: white;
    padding: 4px 8px;
    opacity: 0.7;
    white-space: nowrap;
    font-size: 12px;
  }
  /deep/ .ol-tooltip-measure {
    opacity: 1;
    font-weight: bold;
  }
  /deep/ .ol-tooltip-static {
    background-color: #ffcc33;
    color: black;
    border: 1px solid white;
  }
  /deep/ .ol-tooltip-measure:before,
  /deep/ .ol-tooltip-static:before {
    border-top: 6px solid rgba(0, 0, 0, 0.5);
    border-right: 6px solid transparent;
    border-left: 6px solid transparent;
    content: "";
    position: absolute;
    bottom: -6px;
    margin-left: -7px;
    left: 50%;
  }
  /deep/ .ol-tooltip-static:before {
    border-top-color: #ffcc33;
  }
}
</style>
18.测距+侧面(可删除)
  1. 综合了上面测距和侧面的功能

  2. 增加删除按钮和功能,具体实现如下:

    (1)drawstart 绘画开始阶段:向 measureTooltip 中插入“删除测量”按钮
    (2)drawend 绘画结束阶段:给“删除测量”按钮绑定事件

  3. 缓存绘画时候,缓存所绘制的feature 和 measureTooltip 在 drawCache 对象中

  4. 删除测量,便是从drawCache找到对象的 feature 和 measureTooltip 进行删除,分别调用 removeFeature 和 removeOverlay 方法

<template>
  <div class="map-box">
    <div class="map" id="map"></div>
    <div class="btns-box">
      <el-button type="primary" size="small" @click="measure('LineString')"
        >测量</el-button
      >
      <el-button type="primary" size="small" @click="measure('Polygon')"
        >测面</el-button
      >
      <el-button type="primary" size="small" @click="clearMeasure"
        >清除测量轨迹</el-button
      >
    </div>
  </div>
</template>

<script>
import "ol/ol.css";
import Draw from "ol/interaction/Draw";
import Map from "ol/Map";
import Overlay from "ol/Overlay";
import View from "ol/View";
import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style";
import { LineString, Polygon } from "ol/geom";
import { OSM, Vector as VectorSource } from "ol/source";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import { getArea, getLength } from "ol/sphere";
import { unByKey } from "ol/Observable";
import { fromLonLat } from "ol/proj";

export default {
  data() {
    return {
      layerIndex: 0,
      map: null,
      vector: null,
      sketch: null, //Currently drawn feature
      helpTooltipElement: null, //The help tooltip element.
      helpTooltip: null, //Overlay to show the help messages.
      measureTooltipElement: null, //The measure tooltip element.
      measureTooltip: null, //Overlay to show the measurement.
      draw: null, //global so we can remove it later
      listener: null,
      shapeType: null,
      drawCache: {},
    };
  },

  methods: {
    init() {
      this.vector = new VectorLayer({
        source: new VectorSource(),
        style: new Style({
          fill: new Fill({
            color: "rgba(255, 255, 255, 0.2)",
          }),
          stroke: new Stroke({
            color: "#ffcc33",
            width: 2,
          }),
          image: new CircleStyle({
            radius: 7,
            fill: new Fill({
              color: "#ffcc33",
            }),
          }),
        }),
      });

      //初始化地图
      this.map = new Map({
        target: "map",
        layers: [
          new TileLayer({
            source: new OSM({}),
          }),
          this.vector,
        ],

        view: new View({
          center: fromLonLat([116.40387397, 39.91488908]),
          maxZoom: 18,
          zoom: 13,
          projection: "EPSG:3857",
        }),
      });
    },

    //测距、测面
    measure(type) {
      this.shapeType = type;
      this.layerIndex++;
      this.drawCache[this.layerIndex] = {
        feature: null,
        measureTooltip: null,
      };
      if (this.draw) {
        this.map.removeInteraction(this.draw);
      }
      // 添加map事件
      this.addInteraction();
    },

    //清除测量
    clearMeasure() {
      for (let i in this.drawCache) {
        this.map.removeOverlay(this.drawCache[i].measureTooltip);
        this.vector.getSource().removeFeature(this.drawCache[i].feature);
      }
      this.drawCache = {};
      this.layerIndex = 0;
    },

    addInteraction() {
      this.draw = new Draw({
        source: this.vector.getSource(),
        type: this.shapeType,
        style: new Style({
          fill: new Fill({
            color: "rgba(255, 255, 255, 0.2)",
          }),
          stroke: new Stroke({
            color: "rgba(0, 0, 0, 0.5)",
            lineDash: [10, 10],
            width: 2,
          }),
          image: new CircleStyle({
            radius: 5,
            stroke: new Stroke({
              color: "rgba(0, 0, 0, 0.7)",
            }),
            fill: new Fill({
              color: "rgba(255, 255, 255, 0.2)",
            }),
          }),
        }),
      });

      this.map.addInteraction(this.draw);
      this.createMeasureTooltip();
      this.createHelpTooltip();
      this.drawHandler();
    },

    //处理测距的长度
    formatLength(line) {
      let sourceProj = this.map.getView().getProjection(); //获取投影坐标系
      let length = getLength(line, { projection: sourceProj });
      let output;
      if (length > 100) {
        output = Math.round((length / 1000) * 100) / 100 + " " + "km";
      } else {
        output = Math.round(length * 100) / 100 + " " + "m";
      }
      return output;
    },

    //处理测面的面积
    formatArea(polygon) {
      let sourceProj = this.map.getView().getProjection(); //获取投影坐标系
      let geom = polygon.clone().transform(sourceProj, "EPSG:3857");
      let area = getArea(geom);
      let output;
      if (area > 10000) {
        output =
          Math.round((area / 1000000) * 100) / 100 + " " + "km<sup>2</sup>";
      } else {
        output = Math.round(area * 100) / 100 + " " + "m<sup>2</sup>";
      }
      return output;
    },

    //创建移动提示工具
    createMeasureTooltip() {
      if (this.measureTooltipElement) {
        this.measureTooltipElement.parentNode.removeChild(
          this.measureTooltipElement
        );
      }
      this.measureTooltipElement = document.createElement("div");
      this.measureTooltipElement.className = "ol-tooltip ol-tooltip-measure";
      this.measureTooltip = new Overlay({
        element: this.measureTooltipElement,
        offset: [0, -15],
        positioning: "bottom-center",
      });
      this.map.addOverlay(this.measureTooltip);
    },

    //创建帮助提示工具
    createHelpTooltip() {
      if (this.helpTooltipElement) {
        this.helpTooltipElement.parentNode.removeChild(this.helpTooltipElement);
      }
      this.helpTooltipElement = document.createElement("div");
      this.helpTooltipElement.className = "ol-tooltip hidden";
      this.helpTooltip = new Overlay({
        element: this.helpTooltipElement,
        offset: [15, 0],
        positioning: "center-left",
      });
      this.map.addOverlay(this.helpTooltip);
    },

    //绘制
    drawHandler() {
      //画画开始
      this.draw.on("drawstart", (evt) => {
        this.sketch = evt.feature;
        let tooltipCoord = evt.coordinate;

        this.listener = this.sketch.getGeometry().on("change", (evt) => {
          let output;
          let geom = evt.target;
          if (geom instanceof LineString) {
            output = this.formatLength(geom);
            tooltipCoord = geom.getLastCoordinate();
          } else if (geom instanceof Polygon) {
            output = this.formatArea(geom);
            tooltipCoord = geom.getInteriorPoint().getCoordinates();
          }
          let closeBtn =
            "<i class='tooltip-close-btn tooltip-close-btn_" +
            this.layerIndex +
            "' data-index='" +
            this.layerIndex +
            "'>×</i>";
          this.measureTooltipElement.innerHTML = output + closeBtn;
          this.measureTooltip.setPosition(tooltipCoord);
          this.drawCache[this.layerIndex].measureTooltip = this.measureTooltip;
        });
      });

      //画画
      this.draw.on("drawend", (evt) => {
        this.drawCache[this.layerIndex].feature = evt.feature;
        this.measureTooltipElement.className = "ol-tooltip ol-tooltip-static";
        this.measureTooltip.setOffset([0, -7]);
        this.sketch = null;
        this.measureTooltipElement = null;
        this.createMeasureTooltip();
        unByKey(this.listener);
        this.map.removeInteraction(this.draw);
        this.draw = null;

        // 删除图层
        let self = this;
        document
          .querySelector(".tooltip-close-btn_" + this.layerIndex)
          .addEventListener("click", function () {
            self.vector
              .getSource()
              .removeFeature(self.drawCache[this.dataset.index].feature);
            self.map.removeOverlay(
              self.drawCache[this.dataset.index].measureTooltip
            );
            delete self.drawCache[this.dataset.index];
          });
      });
    },
  },

  mounted() {
    this.init();
  },
  
};
</script>

<style lang="less" scoped>
.map-box {
  width: 100vw;
  height: 100vh;
  position: relative;
  .map {
    height: 100%;
  }
  .btns-box {
    position: absolute;
    right: 20px;
    top: 20px;
  }
}
.map {
  /deep/ .ol-tooltip {
    position: relative;
    background: rgba(0, 0, 0, 0.5);
    border-radius: 4px;
    color: white;
    padding: 4px 14px 4px 8px;
    opacity: 0.7;
    white-space: nowrap;
    font-size: 12px;
  }
  /deep/ .ol-tooltip-measure {
    opacity: 1;
    font-weight: bold;
  }
  /deep/ .ol-tooltip-static {
    background-color: #ffcc33;
    color: black;
    border: 1px solid white;
  }
  /deep/ .ol-tooltip-measure:before,
  /deep/ .ol-tooltip-static:before {
    border-top: 6px solid rgba(0, 0, 0, 0.5);
    border-right: 6px solid transparent;
    border-left: 6px solid transparent;
    content: "";
    position: absolute;
    bottom: -6px;
    margin-left: -5px;
    left: 50%;
  }
  /deep/ .ol-tooltip-static:before {
    border-top-color: #ffcc33;
  }

  //测面、测距的样式
  /deep/ .tooltip-close-btn {
    font-style: normal;
    position: absolute;
    color: #2d8cf0;
    right: 0px;
    top: -7px;
    font-weight: bold;
    font-size: 15px;
    cursor: pointer;
    &:hover {
      color: #fff;
    }
  }
}
</style>
19.多地图切换 (OSM 高德 百度)

踩坑:

也可以有很多种写法,但是不要每次调用Init方法,因为每次都会new一个新的map对象,所以会出现多个地图,我们此处可以去官网下查找其他更改数据源的方法,我此处用的是addLayer()方法

<template >
  <div class="box">
    <div id="map"></div>
    <div class="btn">
      <el-button @click="createMap">OSM</el-button>
      <el-button @click="create_GaoDe_Map">高德</el-button>
      <el-button @click="createMapBaidu">百度</el-button>
    </div>
  </div>
</template>
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import Tile from "ol/layer/Tile";
import { TileImage, OSM } from "ol/source";
import TileGrid from "ol/tilegrid/TileGrid";
import { fromLonLat } from "ol/proj";
import XYZ from "ol/source/XYZ";
export default {
  data() {
    return {
      map: null,
      sourceLayer: null, //图层
    };
  },
  methods: {
    init() {
      this.map = new Map({
        target: "map",
        layers: [
          new Tile({
            source: new OSM(), //默认为OSM地图
          }),
        ],
        view: new View({
          center: fromLonLat([116.40387397, 39.91488908]),
          zoom: 12,
          maxZoom: 25,
          projection: "EPSG:3857",
        }),
      });
    },

    //初始化OSM地图
    createMap() {
      let layer = new Tile({
        source: new OSM(),
      });
      this.map ? this.map.addLayer(layer) : null;
    },

    //初始化高德地图
    create_GaoDe_Map() {
      let layer = new Tile({
        source: new XYZ({
          url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",
        }),
      });
      this.map ? this.map.addLayer(layer) : null;
    },

    //初始化百度地图
    createMapBaidu() {
      let resolutions = [];
      for (let i = 0; i < 19; i++) {
        resolutions[i] = Math.pow(2, 18 - i);
      }
      let tilegrid = new TileGrid({
        origin: [0, 0],
        resolutions: resolutions,
      });
      let baidu_source = new TileImage({
        projection: "EPSG:3857",
        tileGrid: tilegrid,
        tileUrlFunction: function (tileCoord) {
          if (!tileCoord) {
            return "";
          }
          let z = tileCoord[0];
          let x = tileCoord[1];
          let y = -tileCoord[2] - 1;
          if (x < 0) {
            x = "M" + -x;
          }
          if (y < 0) {
            y = "M" + -y;
          }
          return (
            "http://online3.map.bdimg.com/onlinelabel/?qt=tile&x=" +
            x +
            "&y=" +
            y +
            "&z=" +
            z +
            "&styles=pl&udt=20151021&scaler=1&p=1"
          );
        },
      });

      let newlayer = new Tile({
        source: new XYZ({
          url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",
        }),
      });

      let layer = new Tile({
        source: baidu_source,
      });
      console.log(layer);

      this.map ? this.map.addLayer(newlayer) : null;
    },
  },
  mounted() {
    this.init();
  },
};
</script>

<style scoped lang="less">
.box {
  width: 100vw;
  height: 100vh;
  // position: relative;
  #map {
    width: 100%;
    height: 100%;
  }
  .btn {
    position: fixed;
    top: 10px;
    right: 10px;
  }
}
</style>

20.导出地图
<template>
<div class="map-box">
    <div class="map" id="map"></div>
    <div class="export-btn">
        <a id="image-download" download="map.png"></a>
        <el-button type="primary" size="small" @click="exportPNG">Download PNG</el-button>
    </div>
    </div>
</template>

<script>
    import "ol/ol.css";
    import Map from "ol/Map";
    import View from "ol/View";
    import { OSM } from "ol/source";
    import { Tile as TileLayer } from "ol/layer";
    import { fromLonLat } from "ol/proj";
    export default {
        data() {
            return {
                map: null,
            };
        },

        methods: {
            init() {
                this.map = new Map({
                    target: "map",
                    layers: [
                        new TileLayer({
                            source: new OSM({ crossOrigin: "anonymous" }),
                        }),
                    ],

                    view: new View({
                        center: fromLonLat([113.0567, 23.67537]),
                        maxZoom: 25,
                        zoom: 13,
                        projection: "EPSG:3857",
                    }),
                });
            },

            //将地图导出为PNG
            exportPNG() {
                this.map.once("rendercomplete", () => {
                    let mapCanvas = document.createElement("canvas");
                    let size = this.map.getSize();
                    mapCanvas.width = size[0];
                    mapCanvas.height = size[1];
                    let mapContext = mapCanvas.getContext("2d");
                    Array.prototype.forEach.call(
                        document.querySelectorAll(".ol-layer canvas"),
                        function (canvas) {
                            if (canvas.width > 0) {
                                let opacity = canvas.parentNode.style.opacity;
                                mapContext.globalAlpha = opacity === "" ? 1 : Number(opacity);
                                mapContext.drawImage(canvas, 0, 0);
                            }
                        }
                    );
                    if (navigator.msSaveBlob) {
                        navigator.msSaveBlob(mapCanvas.msToBlob(), "map.png");
                    } else {
                        let link = document.getElementById("image-download");
                        link.href = mapCanvas.toDataURL();
                        link.click();
                    }
                });
                this.map.renderSync();
            },
        },

        mounted() {
            this.init();
        },
    };
</script>

<style lang="less" scoped>
    .map-box {
        width: 100vw;
        height: 100vh;
        // height: 100%;
        position: relative;
        .map {
            width: 100%;
            height: 100%;
        }
        .export-btn {
            position: absolute;
            right: 15px;
            top: 15px;
        }
    }
</style>

21.阻止双击放大地图事件
import DoubleClickZoom from "ol/interaction/DoubleClickZoom";

clearDouble() {
    let dblClickInteraction = this.map
    .getInteractions()
    .getArray()
    .find((interaction) => {
        return interaction instanceof DoubleClickZoom;
    });
    this.map.removeInteraction(dblClickInteraction);
},
22.openlayers之地图图层数据来源(ol.source)详解

source 是 Layer 的重要组成部分,表示图层的来源,也就是服务地址。除了在构造函数中制定外,可以使用 layer.setSource(source) 稍后指定。

一、包含的类型

上面介绍的都是可以实例化的类,还有一部分基类,不能被实例化,只负责被继承,有:

ol.source.Image,提供单一图片数据的类型,直接继承自 ol.source.Source;
ol.source.Tile,提供被切分为网格切片的图片数据,继承自 ol.source.Source;
ol.source.Vector,提供矢量图层数据,继承自 ol.source.Source;

二、用法说明
1. ol.source.Vector
矢量图层的数据来源
1.1 事件
包含四个事件,addfeature,changefeature,clear,removefeature。
addfeature,当一个要素添加到 source 中触发; 
changefeature,当要素变化时触发; 
clear,当 source 的 clear 方法调用时候触发; 
removefeature,当要素移除时候发生。

1.2 参数
/**
 * @typedef {{attributions: (Array.<ol.Attribution>|undefined),
 *     features: (Array.<ol.Feature>|undefined),
 *     format: (ol.format.Feature|undefined),
 *     loader: (ol.FeatureLoader|undefined),
 *     logo: (string|olx.LogoOptions|undefined),
 *     strategy: (ol.LoadingStrategy|undefined),
 *     url: (string|undefined),
 *     wrapX: (boolean|undefined)}}
 * @api
 */
attribution,地图右下角的 logo 包含的内容;
features,地理要素,从字符串读取的数据;
format,url属性设置后,加载要素使用的数据格式,采用异步的 AJAX 加载;
loader,加载要素使用的加载函数;
logo,logo包含的内容;
strategy,加载要素使用的策略,默认是 一次性加载所有要素;
url,要素数据的地址;
wrapX,是否在地图水平坐标轴上重复,默认是 true1.3实例
features 方法 
假如有一个包含空间数据的字符串,geojsonObject,是GeoJSON字符串格式,那么可以用来初始化一个图层。
var vectorSource = new ol.source.Vector({
  features: (new ol.format.GeoJSON()).readFeatures(geojsonObject)
 });
var vectorLayer = new ol.layer.Vector({
    source: vectorSource,
    style: style
});
map.addLayer(vectorLayer);

url + format 方法 
如果有一个文件作为数据源,那么可以配置 url 属性来加载数据:
var vectorLayer = new ol.layer.Vector({
  source: new ol.source.Vector({
    url: 'data/geojson/countries.geojson',
    format: new ol.format.GeoJSON()
  })
这两种方法中都会指定数据来源格式, 矢量数据源支持的格式包含很多:gml、EsriJSON、geojson、gpx、igc、kml、osmxml、ows、polyline、topojson、wfs、wkt、wms capabilities(兼容 wms 的格式)、 wms getfeatureinfo、 wmts capabilities、xlink、xsd等格式。这些格式都有readFeatures 、readFeature 和readGeometry 方法用于读取数据。
    
2. ol.source.Tile
提供被切分为切片的图片地图数据 
可配置的选项

/**
 * @typedef {{attributions: (Array.<ol.Attribution>|undefined),
 *            extent: (ol.Extent|undefined),
 *            logo: (string|olx.LogoOptions|undefined),
 *            opaque: (boolean|undefined),
 *            tilePixelRatio: (number|undefined),
 *            projection: ol.proj.ProjectionLike,
 *            state: (ol.source.State|undefined),
 *            tileGrid: (ol.tilegrid.TileGrid|undefined),
 *            wrapX: (boolean|undefined)}}
 */
与 vector 一样的选项就不介绍了,介绍与 vector 特有的选项:

extent,地图视图默认的坐标范围;
opaque,不透明与否,一个布尔值,默认 false;
tilePixelRatio,切片的大小调整选项,如 256 × 256,和 512 × 512;
projection,投影;
state,地图所处的状态,包括undefined,loading,ready,error;
tileGrid,覆盖在地图上的格网;
接受的事件
tileloadstart,切片开始加载时触发的事件;
tileloadend,切片加载完毕触发事件;
tileloaderror,切片加载出错时的处理。
    
3. ol.source.Image
提供单一的图片地图。 
可以配置的选项

/**
 * @typedef {{attributions: (Array.<ol.Attribution>|undefined),
 *            extent: (null|ol.Extent|undefined),
 *            logo: (string|olx.LogoOptions|undefined),
 *            projection: ol.proj.ProjectionLike,
 *            resolutions: (Array.<number>|undefined),
 *            state: (ol.source.State|undefined)}}
 */
resolutions,地图分辨率。其他的选项都与以上的一样。
触发的事件

imageloadstart,图片地图开始加载触发的事件;
imageloadend,图片地图加载完毕触发的事件;
imageloaderror,图片地图加载出错时触发的事件。
四、总结
source 是 layer 中必须的选项,定义着地图数据的来源,与数据有关的函数,如addfeature、getfeature等函数都定义在 source 中,而且数据源支持多种格式。
23.Openlayers的类

Ajax:顾名思义,用于实现Ajax功能,只是OpenLayers的开发者们把它单独写到一个类里了,其中用到了Prototype.js框架里的一些东西。同时,设计的时候也考虑了跨浏览器的问题。

BaseTypes:这里定制了OpenLayers中用到的string,number 和 function。比如,OpenLayers. String. startsWith,用于测试一个字符串是否一以另一个字符串开头;OpenLayers. Number. limitSigDigs,用于限制整数的有效数位;OpenLayers. Function.bind,用于把某一函数绑定于对象等等。

Console:OpenLayers.Console,此名称空间用于调试和把错误等输出到“控制台”上,需要结合使用…/Firebug/firebug.js。

Control:我们通常所说的控件类,它提供各种各样的控件,比如上节中说的图层开关LayerSwitcher,编辑工具条EditingToolbar等等。加载控件的例子:

class = new OpenLayers.Map(‘map’, { controls: [] });

map.addControl(new OpenLayers.Control.PanZoomBar());

map.addControl(new OpenLayers.Control.MouseToolbar());

Events:用于实现OpenLayers的事件机制。具体来说,OpenLayers中的事件分为两种,一种是浏览器事件,例如mouseup,mousedown之类的;另外一种是自定义的,如addLayer之类的。OpenLayers中的事件机制是非常值得我们学习的,后面将具体讨论。

Feature:我们知道:Feature是geography 和attributes的集合。在OpenLayers中,特别地OpenLayers.Feature 类由一个Feature和一个lonlat组成。

Format:此类用于读/写各种格式的数据,它的子类都分别创建了各个格式的解析器。这些格式有:XML、GML、GeoJSON、GeoRSS、JSON、KML、WFS、WKT(Well-Known Text)。

Geometry:怎么翻译呢,几何?是对地理对象的描述。它的子类有Collection、Curve、LinearRing、LineString、MultiLineString、MultiPoint、MultiPolygon、Point、Polygon、Rectangle、Surface,正是这些类的实例,构成了我们看到的地图。需要说明的是,Surface 类暂时还没有实现。

Handler:这个类用于处理序列事件,可被激活和取消。同时,它也有命名类似于浏览器事件的方法。当一个handler 被激活,处理事件的方法就会被注册到浏览器监听器listener ,以响应相应的事件;当一个handler被取消,这些方法在事件监听器中也会相应的被取消注册。Handler通过控件control被创建,而control通过icon表现。

Icon:在计算机屏幕上以图标的形式呈现,有url、尺寸size和位置position 3个属性。一般情况,它与 OpenLayers.Marker结合应用,表现为一个Marker。

Layer:图层。

Map:网业中动态地图。它就像容器,可向里面添加图层Layer和控件Control。实际上,单个Map是毫无意义的,正是Layer和Control成就了它。

Marker:它的实例是OpenLayers.LonLat 和OpenLayers.Icon的集合。通俗一点儿说,Icon附上一定的经纬度就是Marker。

Popup:地图上一个小巧的层,实现地图“开关”功能。使用例子:

Class = new OpenLayers.Popup(“chicken”,

new OpenLayers.LonLat(5,40),

new OpenLayers.Size(200,200),“example popup”,true);

map.addPopup(popup);

Renderer :渲染类。在 OpenLayers 中,渲染功能是作为矢量图层的一个属性存在的,我们称之为渲染器,矢量图层就是通过这个渲染器提供的方法将矢量数据显示出来。
Tile:设计这个类用于指明单个“瓦片”Tile,或者更小的分辨率。Tiles存储它们自身的信息,比如url和size等。

24.GeoJSON介绍

GeoJSON是一种用于编码各种地理数据结构的格式

{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [125.6, 10.1]
  },
  "properties": {
    "name": "Dinagat Islands"
  }
}

以GeoJSON支持以下几何类型:PointLineStringPolygonMultiPointMultiLineString,和MultiPolygon。具有附加属性的几何对象是Feature对象。特征集由FeatureCollection对象包含。

GeoJSON 规范 (RFC 7946)

2015 年,互联网工程任务组 (IETF) 与原始规范作者一起组成了GeoJSON WG来标准化 GeoJSON。 RFC 7946于 2016 年 8 月发布,是 GeoJSON 格式的新标准规范,取代了 2008 GeoJSON 规范。

25.解析GeoJSON数据

有两种概况,一个是安装包或者文件夹解析成GeoJSON数据,这个里面必须报好一个.shp文件作为入口.或者是.getjson文件.

第一种情况:

1. yarn add shpjs 
2. const shapefile = require("shpjs");
shapefile("http://127.0.0.1:5500/国道").then((geojson) => {
    this.fileData = geojson;
});
注意:路径如果读取本地有可能会读取不到并报错,所以我把数据放在了线上
如何放在线上?
使用vscode,下载liveServe插件,然后再文件夹中找到该文件,然后再vscode中启动,并创建一个Html文件,任意命名,因为没有Html文件的话vscode是识别不到的,然后 `shift+!`生成html结构,然后右键有一个open widht live serve 点击它接口生成线上服务,这个vcode窗口不可以关闭,可以最小化,

第二种情况:因为已经是geojson文件了所以我们拿过来直接解析即可

let VectorLayer = new Vector({
    source: new VectorSource({
        projection: "EPSG:4326",
        url: "./simplestyles.geojson",
        format: new GeoJSON(),
        defaultFeatureProjection: "EPSG:4326",
    }),
});
this.map.addLayer(VectorLayer); //添加图层
注意:这个url也需要是线上地址,本地路径有可能会报错
26.openLayers加载ArcGIS服务
因为是矢量数据所以使用Image 也就是投影
let layers = '0,1,2,3,4,'
let ImgLayer = new Image({
    source: new ImageArcGISRest({
        ratio: 1,
        params: {
            layers: `show:${layers}`,
            projection: "EPSG:4490",
        },
        url: "/map/arcgis/rest/services/%E5%BC%A0%E5%AE%B6%E5%8F%A3/%E7%94%B5%E5%AD%90%E5%BA%95%E5%9B%BE/MapServer",
    }),
});
this.map.addLayers()
27.创建天地图,ArcGIS瓦片地图,Bing地图,谷歌地图

注意:这些url中的key都需要自己去相对应的官网去注册 都是不收费的O~

  //创建天地图
    create_sky_map() {
      let tian_di_tu_satellite_layer = new Tile({
        title: "天地图卫星影像", //基本无用
        source: new XYZ({
          url: "arcgisServe?T=ter_w&tk=ley&x={x}&y={y}&l={z}",
        }),
      });

      let tian_di_tu_road_layer = new Tile({
        title: "天地图路网",
        source: new XYZ({
          url: "arcgisServe?T=vec_w&x={x}&y={y}&l={z}&tk=ley",
        }),
      });

      let tian_di_tu_annotation = new Tile({
        title: "天地图文字标注",
        source: new XYZ({
          url: "arcgisServe?T=cva_w&x={x}&y={y}&l={z}&tk=ley",
        }),
      });

      let Layergroup = new LayerGroup({
        layers: [tian_di_tu_road_layer, tian_di_tu_annotation],
      });

      return Layergroup;
    },

    //创建ArcGIS底图
    create_GIS_Map() {
      let url = `https://services.arcgisonline.com/ArcGIS/rest/services/****/MapServer`;
      let Img_laye = new Tile({
        source: new TileArcGISRest({
          ratio: 1,
          params: {},
          url1,
        }),
      });
      return Img_laye;
    },

    //创建Bing地图
    create_Bimg_map() {
      let layer = new Tile({
        title: "Bimg_map",
        source: new BingMaps({
          key: "https://www.bingmapsportal.com/key",
          imagerySet: "AerialWithLabelsOnDemand",
        }),
      });
      return layer;
    },

    //创建谷歌地图
    create_Google() {
      let layer = new Tile({
        title: "谷歌矢量地图服务",
        source: new XYZ({
          url: "http://www.google.cn/maps/vt?lyrs=****n&x={x}&y={y}&z={z}",
        }),
      });
      return layer;
    },
28.openlayers添加弹出层
//创建弹出层
      let queryalert = document.getElementsByClassName("queryAlert")[0]; 
      if (!queryalert) {
        queryalert = document.createElement("div");
        queryalert.className = "queryAlert";
      }
      this.overlay = new Overlay({
        element: queryalert, 
        autoPan: true,
        //autoPanAnimation: {
        //  duration: 250,
        //},
      });
      this.map.addOverlay(this.overlay);
      queryalert.style.display = "block";
      queryalert.innerHTML = DomTemplate;
      this.overlay.setPosition(obj.e.coordinate);
28.openlayers坐标转换

https://spatialreference.org/ref/sr-org/wgs84/proj4/

proj4方法库

import proJ4 from "proj4";
// proJ4(自身的坐标系,要转换的坐标系,投影坐标)
let one = proJ4(proj_4490, proj_4526, newList); //返回一个数组

后续会继续更新…

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐