vue openlayers 的基本使用,以及openlayers+vue基本组件的封装

#写在前面#

最近看了点openlayers的内容。官方api都是英文的,例子也难找。所以把openlayers的基本组件封装了一下方便以后使用。分别封装了 <点标注><折线><多边形><圆形><自定义覆盖物><弹出窗体><海量点> 这几个组件。
GitHub地址:https://github.com/zmannnnn/openlayers | 点击跟踪项目地址

效果展示.jpg

开始

1, 安装

npm install ol

2,建立一个公共的地图配置文件,mapconfig.js(随便丢在哪里都可以,放在main.js一起也行,后面调用)

import TileLayer from 'ol/layer/Tile'
import { OSM, XYZ, TileArcGISRest } from 'ol/source'
const maptype = 2
// 0 表示部署的离线瓦片地图,1表示OSM, 2表示使用Arcgis在线午夜蓝地图服务
const streetmap = function() {
    let maplayer = null
    switch(maptype) {
      case 0:
        maplayer = new TileLayer({
          source: new XYZ({
            url:'http://127.0.0.1:7080/streetmap/shenzhen/{z}/{x}/{y}.jpg'
          })
        })
      break
      case 1:
        maplayer = new TileLayer({
          source: new OSM()
        })
      break
      case 2:
        maplayer = new TileLayer({
          source:new TileArcGISRest({
            url:'https://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunity/MapServer'
          })
      })
      break
    }
    return [ maplayer ]
}
const mapconfig = {
    streetmap: streetmap
}
export default mapconfig

建立openlayers的vue页面

3,建立地图页面(注意开启了esline的要屏蔽掉esline的 no-unused-var 报错)


屏蔽掉esline的 no-unused-var 报错.jpg

vue,openlayers地图页面代码:

<template>
  <div class="openlayer">
    <div id="map" ref="rootmap">
      <!-- 弹出窗体 -->
      <MapPopup :position="mapPopupData.position" :title="mapPopupData.title" :offset="mapPopupData.offset" :mapShow="mapPopupData.show" @close="mapPopupClose" :className="'map-popup'">{{ popupText }}</MapPopup>
      <!-- 点标注,如果只想用文字标注label,可以把图片设置成一个像素的透明图片,如果只想用图标就label设置为null,单独的懒得封装了,-->
      <MapIconMark :position="mapIconData.position" :label="mapIconData.label" :icon='mapIconData.icon' :elementName="mapIconData.elementName" :className="mapIconData.className"></MapIconMark>
      <!-- 折线 -->
      <MapLineString :pointList="mapLineStringData.pointlist" :lineColor="mapLineStringData.lineColor" :lineWidth="mapLineStringData.lineWidth" :lineDash="mapLineStringData.lineDash" :elementName="mapLineStringData.elementName" :className="mapLineStringData.className"></MapLineString>
      <!-- 多边形 -->
      <MapPolygon :pointList="mapPolygonData.pointlist" :fillColor="mapPolygonData.fillColor" :lineColor="mapPolygonData.lineColor" :lineWidth="mapPolygonData.lineWidth" :lineDash="mapPolygonData.lineDash" :elementName="mapPolygonData.elementName" :className="mapPolygonData.className"></MapPolygon>
      <!-- 自定义覆盖物 -->
      <MapOverlay :position="mapOverlayData.position" :className="mapOverlayData.className"><div><img :src="mapOverlayData.img" alt=""></div></MapOverlay>
      <!-- 圆形 -->
      <MapCircle :position="mapCircleData.position" :radius="mapCircleData.radius" :fillColor="mapCircleData.fillColor" :lineColor="mapCircleData.lineColor" :lineWidth="mapCircleData.lineWidth" :lineDash="mapCircleData.lineDash" :elementName="mapCircleData.elementName" :className="mapCircleData.className"></MapCircle>
      <!-- 海量点 -->
      <MapPointCollection :pointList="mapPointCollectionData.pointlist" :distance="mapPointCollectionData.distance" :fillColor="mapPointCollectionData.fillColor" :fontColor="mapPointCollectionData.fontColor" :zIndex='mapPointCollectionData.zIndex' :offset="mapPointCollectionData.offset"></MapPointCollection>
      <!-- pointlist -->
      <!-- 循环使用的方法 -->
      <template v-for="(item,index) of pointlist">
        <MapIconMark :position="item" :icon='iconImg' :key="index" :label="'标记点' + String(index)"></MapIconMark>
      </template>
      <!-- 路径示例 -->
      <MapLineString :pointList="pointlist" :lineColor="'red'" :lineWidth="5" :lineDash="null" :elementName="'轨迹'" :className="'map-road'"></MapLineString>
    </div>
    <!-- <MapPolyLine :center="pointlist"></MapPolyLine> -->
    <div class="click-center">{{clickCenter}}</div>
  </div>
</template>
<script>
// @ is an alias to /src
import 'ol/ol.css'
import { Map, View } from 'ol'
import * as olControl from 'ol/control'
import MapPopup from '@/components/MapPopup'
import MapIconMark from '@/components/MapIconMark'
import MapLineString from '@/components/MapLineString'
import MapPolygon from '@/components/MapPolygon'
import MapCircle from '@/components/MapCircle'
import MapOverlay from '@/components/MapOverlay'
import MapPointCollection from '@/components/MapPointCollection'
import mapconfig from '@/mapconfig'
export default {
  name:'openlayer',
  components: {
      MapPopup,
      MapIconMark,
      MapLineString,
      MapPolygon,
      MapCircle,
      MapOverlay,
      MapPointCollection
    },
  data() {
    return {
      // 弹出窗体图层数据
      mapPopupData:{
        position: [ 114.06919853061095, 22.52312915135971 ], // 弹窗中心点 Array[array], 必须
        title: '弹窗标题', // 弹窗标题 String,非必须,默认为 ' '
        show: false, // 弹窗显隐 Boolean,必须,默认为 true
        offset:[0, 0], // 弹窗偏移 Array[number],必须,默认为 [0, 0]
        className: 'map-popup' // 图层的class String,非必须,默认为 'map-popup'
      },
      // 点标注图层数据
      mapIconData:{
        position: [ 114.0744662014354, 22.52087080791021 ], // 标注中心点 Array, 必须
        icon: require('@/assets/logo.png'), // 文件地址 String[url] ,必须 ,默认为 null,
        label: '这个是中心位置', // 标注点名称 String, 非必须, 默认为 null
        elementName: '点标识ID', // 标注点识别名称 String, 可以通过 feature.get('name') 获取到, 非必须, 默认为 'el-mapIconMark'
        className: 'map-icon-mark' // 图层的class String, 非必须,默认为 'map-icon-mark'
      },
      // 折线图层数据
      mapLineStringData:{
        pointlist:[
          [ 114.07167206985183, 22.5232403088372 ],
          [ 114.07770027654792, 22.523326106333492 ],
          [ 114.07195557462218, 22.518950434022546 ],
          [ 114.07445489299245, 22.52480331540012 ],
          [ 114.07624544943681, 22.519340980469103 ],
          [ 114.07167206985183, 22.5232403088372 ]
        ], // 线条所有的点数组 Array[array], 必须
        elementName: '地图线条', //弹窗标识别名 String, 非必须,默认为 'el-mapLineString'
        lineColor: 'rgba(0,77,168,0.9)', // 线条颜色 String,非必须,默认为 '#409eff'
        lineWidth: 2, // 线条宽度 Number,非必须,默认为 2
        lineDash: [10], // 虚线 Array[number], 是否使用虚线,默认为 null
        className: 'map-line-string', // 图层的class String, 非必须, 默认 "map-line-string"
        zIndex: 300 // 图层z轴高度, 非必须, 默认 300
      },
      // 多边形图层数据
      mapPolygonData:{
        pointlist:[
          [ 114.06763153723739, 22.523766343504477 ],
          [ 114.07099169050478, 22.523803784736963 ],
          [ 114.06770308108405, 22.521172327154904 ],
          [ 114.0693165842837, 22.524608688036995 ],
          [ 114.07017607779578, 22.521397632712155 ]
        ],
        fillColor: 'rgba(255,0,0,0.8)', // 多边形填充颜色,非必须,默认为 'rgba(0,0,0,0.8)'
        elementName: '地图多边形', // 多边形识别名称 String, 非必须,默认为 'el-mapPolygon'
        lineColor: 'rgba(0,0,0,0.5)', // 多边形线条颜色 String,非必须,默认为 '#409eff'
        lineWidth: 2,// 多边形线条宽度 Number,非必须,默认为 2
        lineDash: null, // 多边形虚线 Array[number], 是否使用虚线 ,默认为 null
        className: 'map-polygon' // 图层的class String, 非必须,默认为 'map-polygon'
      },
      // 圆形图层数据
      mapCircleData:{
        position: [ 114.07272943851653, 22.52535977514209 ], // 圆中心点 Array, 必须
        radius: 100, // 圆半径 number ,默认为 100
        fillColor: 'rgba(255,255,255,0.5)', // 圆形填充颜色,非必须,默认为 'rgba(255,255,255,0.5)'
        elementName: '圆形叠加', // 圆形识别名称 String, 非必须,默认为 'el-mapCircle'
        lineColor: '#409eff', // 圆形线条颜色 String,非必须,默认为 '#409eff'
        lineWidth: 2,// 圆形线条宽度 Number,非必须,默认为 2
        lineDash: [20, 5], // 圆形虚线 Array[number], 是否使用虚线 ,默认为 null
        className: 'map-circle' // 图层的class String, 非必须,默认为 'map-circle'
      },
      // 自定义图层数据
      mapOverlayData:{
        position: [ 114.07253789792126, 22.524477970304865 ], // 标注中心点 Array, 必须
        className: 'map-overlay', // 设置自定义图层的class String ,非必须, 默认 'map-overlay'
        offset:[200,200], // 设置自定义图层的偏移量 Array[number] ,非必须,默认[0, 0]
        img: require('@/assets/hot.gif') // slot
      },
      // 海量点图层数据
      mapPointCollectionData:{
        pointlist:[
          [ 114.06923493949402, 22.5240832382725 ],
          [ 114.0682861161323, 22.523565093192584 ],
          [ 114.06995237746318, 22.523445526204714 ],
          [ 114.0685676099809, 22.522193726398406 ],
          [ 114.06969098007714, 22.522269498868212 ]
        ],
        distance: 100, // 收起点的间距  number,必须,默认为 40
        zIndex: 500, // 图层z轴高度, 非必须, 默认 400
        offset:[0, 2], // 文字偏移距离 [x,y], 非必须, 默认 [0,0]
        fontColor: '#ffeb00', // 文字的颜色 string (色彩标识,支持rgba),默认'#fff'(如果去掉文字那么直接rgba透明度设置为0)
        fillColor: '#06d073', // 文字的背景颜色 string(色彩标识,支持rgba),默认'#f00'(如果去不要背景颜色那么直接rgba透明度设置为0)
        bgImg: require('@/assets/mark.png') // 设置背景图,如果设置了此那么文字背景可以不设置
      },
      mapData: null,
      mapCenter: [ 114.07228950670621, 22.524837614865916 ],
      mapZoom: 18,
      clickCenter: [0,0],
      popupText: '弹窗初始化文字',
      iconImg: require('@/assets/mark.png'), // 文件地址 String[url]
      pointlist:[
        [ 114.07871607950588, 22.52222782549443 ],
        [ 114.07844961562236, 22.52315316542209 ],
        [ 114.07696995439895, 22.524754342150676 ],
        [ 114.08107178741518, 22.524136654355292 ]
      ]
    }
  },
  mounted(){
     this.initMap()
  },
  methods:{
    initMap(){
      const mapContainer = this.$refs.rootmap
      const FullScreen = new olControl.FullScreen() // 全屏控件
      const map = new Map({
        layers: mapconfig.streetmap(),
        controls: [FullScreen],
        target: mapContainer,
        view: new View({
          projection: "EPSG:4326",
          center: this.mapCenter,
          zoom: this.mapZoom
        })
      })
      // 添加鼠标点击事件
      map.on('click', this.mapClick)
      // 添加鼠标经过事件
      map.on('pointermove',this.mapPointerMove)
      // 保存地图
      this.mapData = map
    },
    // 鼠标点击地图事件
    mapClick(evt){
      // 获取点击中心点
      this.clickCenter = evt.coordinate
      // 移动地图
      this.mapData.getView().animate({
        center:evt.coordinate,
      })
      // 这个做了一个点击改变中心点的操作
      setTimeout(()=>{
        this.mapIconData.position = [ 114.07438338675577, 22.522278765283236 ]
        this.mapData.getView().animate({
          center:[ 114.07438338675577, 22.522278765283236 ],
        })
      },5000)
    },
    // 鼠标划过地图事件
    mapPointerMove(evt){
       if (evt.dragging) {
        return
      }
      // 获取地图上的重叠像素(用来获得叠加图层)
      const pixel = this.mapData.getEventPixel(evt.originalEvent)
      const hit = this.mapData.hasFeatureAtPixel(pixel)
      // 获取地图上的feature
      const feature = this.mapData.forEachFeatureAtPixel(evt.pixel, function (feature, layer) {
        return feature
      })
      // 获取叠加图层对像素叠加图层(hit)设置鼠标样式(给标注点添加鼠标经过样式)
      this.mapData.getTarget().style.cursor = hit ? 'pointer' : ''
      // 鼠标移动到点标注的时候显示弹出窗体,feature.get('name') 可以获取标注标题,如果设置了的话
      if(feature && feature.get('name') === this.mapIconData.elementName){
        // 显示弹出窗体
        this.mapPopupData.show = true
        // 弹出窗体的内容
        this.popupText = `当前坐标${this.mapIconData.position}`
        // 弹出窗体的位置
        this.mapPopupData.position = this.mapIconData.position
      }
    },
    // 关闭弹出窗体事件
    mapPopupClose(e){
      this.mapPopupData.show = false
      this.popupText = ''
    }
  }
}
</script>
<style lang="scss" scoped>
.openlayer{
  height: 100vh;
  width: 100vw;
}
#map{
  height: 100%;
  width: 100vw;
  background: #f2f2f2;
}
.click-center{
  position: absolute;
  top: 10px;
  right: 60px;
  padding: 10px;
  z-index: 2;
  background: rgba($color: #000000, $alpha: .5);
  color: #fff;
  border-radius: 4px;
}
</style>

组件

4.1,组件<点标注>(MapIconMark.vue)

<script>
// 点标注组件
import { Vector as SourceVec, Cluster} from 'ol/source'
import { Feature } from 'ol'
import { Point } from 'ol/geom'
import { Style, Icon, Stroke, Fill, Text } from 'ol/style'
import { Vector as LayerVec } from 'ol/layer'
export default {
  name: 'MapIcon',
  render () {
    return this.$parent.preventChildrenRender
  },
  props: {
    position: { type: Array },
    elementName: { type: String },
    className: { type: String },
    label: { type: String },
    icon: { type: String },
    zIndex: { type: Number }
  },
  data(){
    return {
      iconLayer: null
    }
  },
  watch:{
    position:{
      handler (newVal, oldVal) {
        this.MapIconMark()
      },
      deep: true
    },
    elementName:{
      handler (newVal, oldVal) {
        this.MapIconMark()
      },
      deep: true
    },
    className:{
      handler (newVal, oldVal) {
        this.MapIconMark()
      },
      deep: true
    },
    label:{
      handler (newVal, oldVal) {
        this.MapIconMark()
      },
      deep: true
    },
    icon:{
      handler (newVal, oldVal) {
        this.MapIconMark()
      },
      deep: true
    },
    zIndex:{
      handler () {
        this.MapIconMark()
      },
      deep: true
    }
  },
  mounted(){
    this.$nextTick(()=>{
      this.MapIconMark()
    })
  },
  methods:{
    // 单个标注
    MapIconMark() {
      const _that = this
      if(_that.iconLayer){
        _that.iconLayer.getSource().clear()
      }
      // 创建矢量容器
      const vectorSource = new SourceVec({})
      //创建图标特性
      const iconFeature = new Feature({
        type: 'icon',
        name: _that.elementName || 'el-mapIconMark',
        geometry: new Point(this.position),
      })
      // 图标特性添加到矢量容器
      vectorSource.addFeature(iconFeature)
      //创建矢量层
      _that.iconLayer = new LayerVec({
        className: _that.className || 'map-icon-mark',
        source: vectorSource,
        zIndex: _that.zIndex || 800,
        //创建图标样式
        style: new Style({
          image: new Icon({
            src: _that.icon || null
          }),
          // 文本样式
          text:new Text({
            text: _that.label || null,
            fill: new Fill({
              color: '#fff',
            }),
            font:'14px Microsoft YaHei',
            offsetX: 0,
            offsetY: 30,
            padding:[ 2, 10, 0, 10],
            // 文本边框
            /*
            backgroundStroke: new Stroke({
              color: '#f00',
              width: 1,
            }),
            */
            // 文本填充
            backgroundFill: new Fill({
              color:'rgba(0,0,0,0.5)'
            })
          })
        })
      })
      _that.$parent.$data.mapData.addLayer(_that.iconLayer)
    }
  }
}
</script>

4.2,组件<折线>(MapLineString.vue)

<script>
// 折线组件
import { Vector as SourceVec, Cluster } from "ol/source"
import { Feature } from "ol";
import { Style, Stroke, Fill } from "ol/style"
import { Vector as LayerVec } from "ol/layer"
import { LineString } from 'ol/geom'
export default {
  name: "MapLineString",
  render() {
    return this.$parent.preventChildrenRender;
  },
  props: {
    pointList: { type: Array },
    elementName: { type: String },
    className: { type: String },
    lineColor: { type: String },
    lineWidth: { type: Number },
    lineDash: {tyep: Array },
    zIndex: { type: Number }
  },
  data() {
    return{
      lineStringLayer: null
    }
  },
   watch:{
    pointList:{
      handler () {
        this.MapLineString()
      },
      deep: true
    },
    elementName:{
      handler () {
        this.MapLineString()
      },
      deep: true
    },
    className:{
      handler () {
        this.MapLineString()
      },
      deep: true
    },
    lineColor:{
      handler () {
        this.MapLineString()
      },
      deep: true
    },
    lineDash:{
      handler () {
        this.MapLineString()
      },
      deep: true
    },
    lineWidth:{
      handler () {
        this.MapLineString()
      },
      deep: true
    },
    zIndex:{
      handler(){ 
        this.MapLineString()
      }
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.MapLineString()
    });
  },
  methods: {
    MapLineString() {
      const _that = this
      // 清除原来的
      if(_that.lineStringLayer){
        _that.lineStringLayer.getSource().clear()
      }
      // 创建一个 source图层
      const source = new SourceVec();
      // 所有点共同构成一个 feature
      const feature = new Feature({
        name: _that.elementName || null,
        geometry: new LineString(this.pointList),
      });
      // 然后把feature添加到source里
      source.addFeature(feature);
      _that.lineStringLayer = new LayerVec({
        className: _that.className || "map-line-string",
        source: source,
        zIndex: _that.zIndex || 300,
        style: function (feature, resolution) {
          // console.log(feature)
          const style = [
            new Style({
              stroke: new Stroke({
                color: _that.lineColor || '#409eff',
                width: _that.lineWidth || 2,
                lineDash: _that.lineDash || null,
                lineDashOffset: 0
              })
            }),
          ];
          return style;
        },
      });
      _that.$parent.$data.mapData.addLayer(_that.lineStringLayer)
    },
  },
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>

4.3,组件<多边形>(MapPolygon.vue)

<script>
// 多边形组件
import { Vector as SourceVec, Cluster } from "ol/source"
import { Feature } from "ol"
import { Style, Stroke, Fill } from "ol/style"
import { Vector as LayerVec } from "ol/layer"
import { Polygon } from 'ol/geom'
export default {
  name: "MapPolygon",
  render() {
    return this.$parent.preventChildrenRender;
  },
  props: {
    pointList: { type: Array },
    elementName: { type: String },
    className: { type: String },
    lineColor: { type: String },
    lineWidth: { type: Number },
    lineDash: { tyep: Array },
    fillColor: { type :String },
    zIndex: { type: Number }
  },
  data() {
    return{
      polygonLayer: null
    }
  },
  watch: {
    pointList:{
      handler () {
        this.MapPolygon()
      },
      deep: true
    },
    elementName:{
      handler () {
        this.MapPolygon()
      },
      deep: true
    },
    className:{
      handler () {
        this.MapPolygon()
      },
      deep: true
    },
    lineColor:{
      handler () {
        this.MapPolygon()
      },
      deep: true
    },
    lineWidth:{
      handler () {
        this.MapPolygon()
      },
      deep: true
    },
    lineDash:{
      handler () {
        this.MapPolygon()
      },
      deep: true
    },
    fillColor:{
      handler () {
        this.MapPolygon()
      },
      deep: true
    },
    zIndex:{
      handler(){
        this.MapPolygon()
      }
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.MapPolygon()
    });
  },
  methods: {
    MapPolygon() {
      const _that = this
      // 清除原来的
      if(_that.polygonLayer){
        _that.polygonLayer.getSource().clear()
      }
      // 创建一个 source图层
      const source = new SourceVec()
      // 所有点共同构成一个 feature
      const feature = new Feature({
        name: _that.elementName || 'el-mapPolygon',
        geometry: new Polygon([this.pointList]),
      })
      // 然后把feature添加到source里
      source.addFeature(feature)
      this.polygonLayer = new LayerVec({
        className: _that.className || "map-polygon",
        source: source,
        zIndex: _that.zIndex || 200,
        style: function (feature, resolution) {
          const style = [
            new Style({
              stroke: new Stroke({
                color:  _that.lineColor || '#409eff',
                width: _that.lineWidth || 2,
                lineDash: _that.lineDash || null,
                lineDashOffset: 0
              }),
              //填充样式
              fill: new Fill({
                color: _that.fillColor || 'rgba(0,0,0,0.8)' //颜色、渐变或图案
              }),
            }),
          ];
          return style;
        },
      })
      this.$parent.$data.mapData.addLayer(this.polygonLayer)
    },
  },
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>

4.4,组件<圆形>(MapCircle.vue)

<script>
// 点标注组件
import { Vector as SourceVec, Cluster} from 'ol/source'
import { Feature } from 'ol'
import { Circle } from 'ol/geom'
import { Style, Icon, Stroke, Fill, Text } from 'ol/style'
import { Vector as LayerVec } from 'ol/layer'
export default {
  name: 'MapIcon',
  render () {
    return this.$parent.preventChildrenRender
  },
  props: {
    position: { type: Array },
    radius: {type: Number },
    elementName: { type: String },
    className: { type: String },
    lineColor: { type: String },
    lineWidth: { type: Number },
    lineDash: { tyep: Array },
    fillColor: { type :String },
    zIndex: { type: Number }
  },
  data(){
    return {
      circleLayer: null
    }
  },
  watch:{
    position:{
      handler () {
        this.MapCircle()
      },
      deep: true
    },
    radius:{
       handler () {
        this.MapCircle()
      },
      deep: true
    },
    elementName:{
      handler () {
        this.MapCircle()
      },
      deep: true
    },
    className:{
      handler () {
        this.MapCircle()
      },
      deep: true
    },
    lineColor:{
      handler () {
        this.MapCircle()
      },
      deep: true
    },
    lineWidth:{
      handler () {
        this.MapCircle()
      },
      deep: true
    },
    lineDash:{
      handler () {
        this.MapCircle()
      },
      deep: true
    },
    fillColor:{
      handler () {
        this.MapCircle()
      },
      deep: true
    },
    zIndex:{
      handler(){
        this.MapCircle()
      }
    }
  },
  mounted(){
    this.$nextTick(()=>{
      this.MapCircle()
    })
  },
  methods:{
    // 圆形叠加图层
    MapCircle() {
      const _that = this
      if(_that.circleLayer){
        _that.circleLayer.getSource().clear()
      }
      // 圆的半径换算
      let metersPerUnit = _that.$parent.$data.mapData.getView().getProjection().getMetersPerUnit()
      let circleRadius =  ( _that.radius || 100) / metersPerUnit
      // 创建矢量容器
      const centerRadius = new Circle(_that.position, circleRadius)
      const vectorSource = new SourceVec({})
      //创建图标特性
      const circleFeature  = new Feature({
        type: 'mapCircle',
        name: _that.elementName || 'el-mapCircle',
        geometry: centerRadius,
      })
      // 图标特性添加到矢量容器
      vectorSource.addFeature(circleFeature)
      //创建矢量层
      _that.circleLayer = new LayerVec({
        className: _that.className || 'map-circle',
        source: vectorSource,
        zIndex: _that.zIndex || 200,
        //创建圆的样式
        style: new Style({
          stroke: new Stroke({
            color:  _that.lineColor || '#409eff',
            width: _that.lineWidth || 2,
            lineDash: _that.lineDash || null,
            lineDashOffset: 0
          }),
          //填充样式
          fill: new Fill({
            color: _that.fillColor || 'rgba(255,255,255,0.5)' // 颜色、渐变或图案
          })
        })
      })
      _that.$parent.$data.mapData.addLayer(_that.circleLayer)
    }
  }
}
</script>

4.5,组件<自定义覆盖物>(MapOverlay.vue)

<template>
  <!-- Overlay start -->
  <div ref="overlayMain" class="overlay-main">
    <slot />
  </div>
  <!-- Overlay end -->
</template>
<script>
// 自定义覆盖物组件
import Overlay from 'ol/Overlay'
export default {
  name: 'MapOverlay',
  props: {
    position: { type: Array },
    className: { type: String },
    offset:{ type: Array }
  },
  data() {
    return {
      dialogOverlay: null
    }
  },
  watch:{
    position: {
      handler (val) {
        this.reload()
      },
      deep: true
    },
    className: {
      handler (val) {
        this.reload()
      },
      deep: true
    },
    offset: {
      handler (val) {
        this.reload()
      },
      deep: true
    }
  },
  mounted(){
    this.$nextTick(()=>{
      this.reload()
    })
  },
  methods:{
    reload(){
      if(this.dialogOverlay) {
        this.$parent.$data.mapData.removeOverlay(this.dialogOverlay)
      }
      this.dialogOverlay = new Overlay({
        element: this.$refs.overlayMain,
        stopEvent: false,
        offset: this.offset || [0, 0],
        className: this.className || 'map-overlay',
      })
      this.dialogOverlay.setPosition(this.position)
      this.$parent.$data.mapData.addOverlay(this.dialogOverlay)
    }
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>

4.6,组件<弹出窗体>(MapPopup.vue)

<template>
  <!-- 弹窗 start -->
  <div ref="mapPopup" class="map-popup-all" v-show="popupShow">
    <div class="map-popup-closer" @click="closePopup">+</div>
    <h3>{{ title || ' ' }}</h3>
    <div id="popup-content"><slot /></div>
  </div>
  <!-- 弹窗 end -->
</template>
<script>
// 弹窗组件
import Overlay from 'ol/Overlay'
export default {
  name: 'MapOverlay',
  props: {
    position: { type: Array },
    className: { type: String },
    mapShow: { type: Boolean },
    title: {type: String },
    offset: { type: Array },
  },
  data() {
    return {
      popupShow: true,
      dialogOverlay: null
    }
  },
  watch:{
    position: {
      handler () {
        this.reload()
      },
      deep: true
    },
    className: {
      handler () {
        this.reload()
      },
      deep: true
    },
    title: {
      handler () {
        this.reload()
      },
      deep: true
    },
    offset: {
      handler () {
        this.reload()
      },
      deep: true
    },
    mapShow:{
      handler(val){
        this.popupShow = val
      },
      deep: true,
      immediate: true
    }
  },
  mounted(){
    this.$nextTick(()=>{
      this.reload()
    })
  },
  methods:{
    reload(){
      if(this.dialogOverlay) {
        this.$parent.$data.mapData.removeOverlay(this.dialogOverlay)
      }
      this.dialogOverlay = new Overlay({
        element: this.$refs.mapPopup,
        stopEvent: false,
        offset: this.offset || [0, 0],
        className: this.className || 'map-popup',
        autoPanAnimation: {
          duration: 250,
        },
      })
      this.dialogOverlay.setPosition(this.position)
      this.$parent.$data.mapData.addOverlay(this.dialogOverlay)
    },
    closePopup(){
      this.$emit('close', false)
    }
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.map-popup-all {
  position: absolute;
  z-index: 400;
  bottom: 20px;
  left: 50%;
  min-width: 150px;
  transform: translateX(-50%);
  background: #fff;
  border-radius: 4px;
  border: 1px solid #ebeef5;
  padding: 12px;
  color: #606266;
  line-height: 1.4;
  text-align: justify;
  font-size: 14px;
  box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
  word-break: break-all;
  h3{
    font-size: 14px;
    margin-top: -4px;
    padding-right: 20px;
  }
  .map-popup-closer{
    position: absolute;
    right: 0;
    top: 0;
    width: 20px;
    text-align: center;
    height: 20px;
    font-size: 26px;
    transform: rotate(45deg);
    color: #c5c5c5;
    cursor: pointer;
  }
  .map-popup-closer:hover{
    color: #888;
  }
}
.map-popup-all::before{
  content: ' ';
  width: 0;
  height: 0;
  border: solid 10px transparent;
  border-top-color: #fff;
  position: absolute;
  bottom: -20px;
  left: 50%;
  transform: translateX(-50%);
}

</style>


4.7,组件<海量点>(MapPointCollection.vue)

<script>
import { Vector as SourceVec, Cluster } from "ol/source";
import { Feature } from "ol";
import { Point } from "ol/geom";
import { Style, Icon, Stroke, Fill, Text, Circle } from "ol/style";
import { Vector as LayerVec } from "ol/layer";
export default {
  name: "MapPointCollection",
  render() {
    return this.$parent.preventChildrenRender;
  },
  props: {
    pointList: { type: Array },
    distance: { type: Number },
    offset: { type: Array },
    fontColor: { type: String },
    fillColor: { type: String },
    zIndex: { type: Number },
    bgImg: { type: String },
    className: { type: String }
  },
  data() {
    return {
      clustersLayer: null,
    };
  },
  watch: {
    pointList: {
      handler() {
        this.MapPointCollection();
      },
      deep: true,
    },
    zIndex: {
      handler() {
        this.MapPointCollection();
      },
      deep: true,
    },
    distance: {
      handler() {
        this.MapPointCollection();
      },
      deep: true,
    },
    fontColor: {
      handler() {
        this.MapPointCollection();
      },
      deep: true,
    },
    fillColor: {
      handler() {
        this.MapPointCollection();
      },
      deep: true,
    },
    bgImg: {
      handler() {
        this.MapPointCollection();
      },
      deep: true,
    },
    offset: {
      handler() {
        this.MapPointCollection();
      },
      deep: true,
    },
    className: {
      handler() {
        this.MapPointCollection();
      },
      deep: true,
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.MapPointCollection();
    });
  },
  methods: {
    // 海量点
    MapPointCollection() {
      const _that = this;
      // 清除原来的
      if (_that.clustersLayer) {
        _that.clustersLayer.getSource().clear();
      }
      // 创建Feature对象集合
      const features = [];
      for (let i = 0; i < _that.pointList.length; i++) {
        features.push(new Feature({ geometry: new Point(_that.pointList[i]) }));
      }
      // 加载聚合标注的矢量图层
      const styleCache = {}; //用于保存特定数量的聚合群的要素样式
      _that.clustersLayer = new LayerVec({
        className: _that.className || "map-point-collection", // 设置容器的class
        zIndex: _that.zIndex || 400,
        // 聚合标注数据源
        source: new Cluster({
          distance: _that.distance || 40,
          // 矢量要素数据源
          source: new SourceVec({
            features: features,
          }),
        }),
        style: function (feature, resolution) {
          const size = feature.get("features").length; // 获取该要素所在聚合群的要素数量
          let style = styleCache[size];
          if (!style) {
            if (_that.bgImg) {
              style = [
                new Style({
                  image: new Icon({
                    src: _that.bgImg,
                  }),
                  // 文本设置
                  text: new Text({
                    text: size.toString(),
                    fill: new Fill({
                      color: _that.fontColor || "#fff",
                    }),
                    font: "14px Microsoft YaHei",
                    offsetX: _that.offset ? _that.offset[0] : 0,
                    offsetY: _that.offset ? _that.offset[1] : 0
                  }),
                }),
              ];
              styleCache[size] = style;
            } else {
              style = [
                new Style({
                  image: new Circle({
                    radius: 10,
                    // 边线
                    stroke: new Stroke({
                      color: "#fff",
                    }),
                    fill: new Fill({
                      color: _that.fillColor || "#f00",
                    }),
                  }),
                  // 文本设置
                  text: new Text({
                    text: size.toString(),
                    fill: new Fill({
                      color: _that.fontColor || "#fff",
                    }),
                    font: "14px Microsoft YaHei",
                    offsetX: _that.offset ? _that.offset[0] : 0,
                    offsetY: _that.offset ? _that.offset[1] : 0
                  }),
                }),
              ];
              styleCache[size] = style;
            }
          }
          return style;
        },
      });
      _that.$parent.$data.mapData.addLayer(_that.clustersLayer);
    },
  },
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>

用法说明

点标注(MapIconMark)
<MapIconMark :position="mapIconData.position" :label="mapIconData.label" :icon='mapIconData.icon' :elementName="mapIconData.elementName" :className="mapIconData.className"></MapIconMark>
/**
* position // 标注点的位置 array [lng, lat]
* label // 标注点的标签 string | null , 非必须 ,默认 null,
* icon // 标注点的图标 string(url) | 如果只要标签名称就设置成一个像素的透明图标, 必须
* elmentName // 通过feature.get('name') 方法可以获得该叠加图层,非必须, 默认 'el-mapIconMark'
* className // 设置class名称, 非必须, 默认 'map-icon-mark'
* zIndex // 图层z轴高度, 非必须, 默认 800
*/

折线(MapLineString)
<MapLineString :pointList="mapLineStringData.pointlist" :lineColor="mapLineStringData.lineColor" :lineWidth="mapLineStringData.lineWidth" :lineDash="mapLineStringData.lineDash" :elementName="mapLineStringData.elementName" :className="mapLineStringData.className"></MapLineString>
/**
* pointList // 组成线的点列表数组 array [[lng, lat],...]
* lineColor // 线条颜色 String,非必须,默认为 '#409eff' 
* lineWidth // 线条宽度 Number,非必须,默认为 2
* lineDash // 虚线 Array[number], 是否使用虚线,默认为 null
* className // 图层的class String, 非必须, 默认 "map-line-string"
* zIndex // 图层z轴高度, 非必须, 默认 300
* elmentName // 通过feature.get('name') 方法可以获得该叠加图层
*/

多边形(MapPolygon)
<MapPolygon :pointList="mapPolygonData.pointlist" :fillColor="mapPolygonData.fillColor" :lineColor="mapPolygonData.lineColor" :lineWidth="mapPolygonData.lineWidth" :lineDash="mapPolygonData.lineDash" :elementName="mapPolygonData.elementName" :className="mapPolygonData.className"></MapPolygon>
/**
* pointList // 组成多边形的点列表数组 array [[lng, lat],...]
* fillColor // 多边形填充颜色,非必须,默认为 'rgba(0,0,0,0.8)'
* elementName // 多边形识别名称 String, 非必须,默认为 'el-mapPolygon'
* lineColor // 多边形线条颜色 String,非必须,默认为 '#409eff'
* lineWidth // 多边形线条宽度 Number,非必须,默认为 2
* lineDash // 多边形虚线 Array[number], 是否使用虚线 ,默认为 null
* className // 图层的class String, 非必须,默认为 'map-polygon'
*/

圆形(MapCircle)
 <MapCircle :position="mapCircleData.position" :radius="mapCircleData.radius" :fillColor="mapCircleData.fillColor" :lineColor="mapCircleData.lineColor" :lineWidth="mapCircleData.lineWidth" :lineDash="mapCircleData.lineDash" :elementName="mapCircleData.elementName" :className="mapCircleData.className"></MapCircle>
/**
* position // 圆中心点 Array, 必须
* radius // 圆半径 number ,默认为 100
* fillColor // 圆形填充颜色,非必须,默认为 'rgba(255,255,255,0.5)'
* elementName // 圆形识别名称 String, 非必须,默认为 'el-mapCircle'
* lineColor // 圆形线条颜色 String,非必须,默认为 '#409eff'
* lineWidth // 圆形线条宽度 Number,非必须,默认为 2
* lineDash // 圆形虚线 Array[number], 是否使用虚线 ,默认为 null
* className // 图层的class String, 非必须,默认为 'map-circle'
*/

自定义覆盖物(MapOverlay)
<MapOverlay :position="mapOverlayData.position" :className="mapOverlayData.className"><div><img :src="mapOverlayData.img" alt=""></div></MapOverlay>
/**
* position // 标注中心点 Array, 必须
* className // 设置自定义图层的class String ,非必须, 默认 'map-overlay'
* offset // 设置自定义图层的偏移量 Array[number] ,非必须,默认[0, 0]
*/

弹出窗体(MapPopup)
<MapPopup :position="mapPopupData.position" :title="mapPopupData.title" :offset="mapPopupData.offset" :mapShow="mapPopupData.show" @close="mapPopupClose" :className="'map-popup'">{{ popupText }}</MapPopup>
/**
* position // 弹窗中心点 Array[array], 必须
* title // 弹窗标题 String,非必须,默认为 ' '
* mapShow // 弹窗显隐 Boolean,必须,默认为 true
* offset // 弹窗偏移 Array[number],必须,默认为 [0, 0]
* className // 图层的class String,非必须,默认为 'map-popup'
* close() // 弹窗关闭事件
*/

海量点(MapPointCollection)
<MapPointCollection :pointList="mapPointCollectionData.pointlist" :distance="mapPointCollectionData.distance" :fillColor="mapPointCollectionData.fillColor" :fontColor="mapPointCollectionData.fontColor" :zIndex='mapPointCollectionData.zIndex' :offset="mapPointCollectionData.offset"></MapPointCollection>
/**
* pointlist // 组成多边形的点列表数组 array [[lng, lat],...]
* distance // 收起点的间距  number,必须,默认为 40
* zIndex // 图层z轴高度, 非必须, 默认 400
* offset // 文字偏移距离 [x,y], 非必须, 默认 [0,0]
* fontColor // 文字的颜色 string (色彩标识,支持rgba),默认'#fff'(如果去掉文字那么直接rgba透明度设置为0)
* fillColor // 文字的背景颜色 string(色彩标识,支持rgba),默认'#f00'(如果去不要背景颜色那么直接rgba透明度设置为0)
* bgImg // 设置背景图,如果设置了此那么文字背景可以不设置
*/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容