【OpenLayers】天地图——实现弹框功能

本文采用天地图的瓦片资源,结合openlayers进行展示。【浏览器端】
技术:vue+ol

1 注册成为开发者

天地图官网:http://lbs.tianditu.gov.cn/home.html
注册为开发者后,创建应用获取key值。本文采用【浏览器端】端key值。image.png

2 使用天地图的瓦片资源

在地图API中,根据需要自行使用。
由图可以看出天地图的地图是由两部分组成的:【底图】、【标记】,所以当我们引用时,我们需要将底图以及对应的标记结合一起使用。
image.png

瓦片资源使用例子:

使用【矢量底图】:vec_c

url:"http://t{0-7}.tianditu.gov.cn/vec_c/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=你的密钥"
//或者
url: "http://t2.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=你的秘钥"

注意:

  • http://t后面的数字,这个表示天地图的服务通道,可以是0,1,2,3,4,5,6,7中的任意一个数字,所以把以上的这个数字改变为这8个数字之一后,不会影响显示效果;
  • T=后面的三个字母。这三个字母就很关键了,表示切片的类型,这三个字母不同,图层显示的内容也不同。

切片类型:
常用的用绿色标记

图层名称底图标记投影类型
矢量(街道)vec_ccva_c经纬度投影
vec_wcva_w球面墨卡托投影
影像img_ccia_c经纬度投影
img_wcia_w球面墨卡托投影
地形ter_ccta_c经纬度投影
ter_wcta_w球面墨卡托投影
全球境界ibo_c
经纬度投影
ibo_w
球面墨卡托投影
矢量英文注记
eva_c经纬度投影

eva_w球面墨卡托投影

公共方法:底图,标记方法

注意: 方法中的参数【layer】是图片资源的切片类型
方法:
获取【地图底图】方法

    // 天地图的瓦片资源
    //获取【地图底图】方法
    getBaseLayer(layer) {
      return new TileLayer({
        source: new XYZ({
          url: `http://t2.tianditu.com/DataServer?T=${layer}&x={x}&y={y}&l={z}&tk=你的密钥`,
        }),
      });
    },

获取【地图标记】方法

    //获取【地图标记】方法
    getMarkerLayer(layer) {
      return new TileLayer({
        source: new XYZ({
          url: `http://t2.tianditu.com/DataServer?T=${layer}&x={x}&y={y}&l={z}&tk=你的密钥`,
        }),
      });
    },


3 弹框功能

前期的基础学习和了解后,我们来实现一个弹窗功能吧~~~
image.png
效果实现:移动地图上的定位icon,弹框也会随着移动并且经纬度也会相应的更新,点击关闭按钮弹框关闭,再次点击图标会再次出现。
大概的实现思路:
在地图上显示标记,也就是在瓦片地图层上新叠加了一层,也就是矢量层;弹窗也是,都是图层的叠加,跟PS的图层效果一样,所以我们要创建矢量层,并且添加到map中就可以了。

  1. 矢量图层中引入图标,然后添加到map中。
  2. 写弹框组件
  3. 挂载在地图上!!
  4. 实现每次移动图标弹框也会随着一起移动。

理清思路之后让我们看看如何实现吧!

3.1 在地图上显示图标

准备: 首先是找一个图标icon,这里我是在阿里图标里面下载的一个png图片。
具体的实现思路:

  1. 创建矢量对象Feature,设置样式里创建Icon对象,然后将矢量对象放在矢量源VectorSource里,再把矢量源放到矢量图层VectorLayer里,最后将矢量图层【定位图标】添加到地图。
  2. 可以拖动效果:创建Translate 对象,将矢量图层加载到此对象的layers中。

OpenLayers中显示一个icon、多边形、线之类的需要矢量对象Feature。创建一个js文件,这里取名feature.js,代码如下:

// 显示一个小icon、多边形、线之类的需要使用矢量对象Feature;
import Feature from "ol/Feature";
import Point from "ol/geom/Point";
import { Vector as VectorLayer } from "ol/layer";
import { Vector as VectorSource } from "ol/source";
import { Style, Icon } from "ol/style";

//支持能拖动要素来修改它的位置,实现这个需要Translate交互的支持
import { Translate } from "ol/interaction";

// 实例化要素
let feature = new Feature({
  geometry: new Point([120.18928024510497, 30.19961553664213]), // 地理几何图形选用点几何
});
// 如果需要给要素附加一些自定义数据
// feature.set("data", data);
// 设置样式,这里就是显示一张图片icon
feature.setStyle([
  new Style({
    image: new Icon({
      anchor: [0.5, 1], // 显示位置
      size: [25, 28], // 尺寸
      src: require("../position.png"), // 图片url
    }),
  }),
]);
// 矢量源
let source = new VectorSource({
  features: [feature],
});
// 实例化的时候也可以不添加feature,后续通过方法添加:source.addFeatures([feature])
// 清空feature:source.clear()

// 矢量图层
export const vector = new VectorLayer({
  source: source,
});
// 样式除了可以设置在单个feature上,也可以统一设置在矢量图层上
/*
let vector = new VectorLayer({
    source: source,
    style: new Style({
        image: new Icon({
          anchor: [0.5, 1],// 显示位置
          size: [18, 28],// 尺寸
          src: require('../../assets/images/mouse_location_ing.png')// 图片url
        })
    })
})
*/

//支持能拖动要素来修改它的位置
export let translate = new Translate({
  layers: [vector],
});

在地图组件OlMap.vue中引入js


// 显示一个小icon图片
import { vector, translate } from "../assets/js/feature.js";

      //显示一个图片icon
      this.map.addLayer(vector);
      // start 支持能拖动要素来修改它的位置
      this.map.addInteraction(translate);
      // 可以监听一下拖动开始和结束的事件,拖动后的经纬度可以从e里面获取
      translate.on("translateend", (e) => {
        console.log(e);
      });
      translate.on("translatestart", (e) => {
        console.log(e);
      });
      // end 支持能拖动要素来修改它的位置

实现效果:
image.png

3.2 创建弹窗组件

效果:image.png
根据图片,写组件MessageTip.vue

<template>
  <div :class="$style['container']">
    <div :class="$style['content']">
      <div :class="$style['titleName']">
        <span>{{ data.title }}</span>
        <span @click="$emit('on-close')" style="padding-left: 125px">X</span>
      </div>
      <div :class="$style['value']">
        <span>经度:{{ data.longitude }}</span
        ><br />
        <span>纬度:{{ data.latitude }}</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "MessageTip",
  components: {},
  filters: {},
  props: {
    data: {
      type: Object,
      //   required: true,
    },
  },
  data() {
    return {
      detail: {},
    };
  },
  created() {},
  methods: {},
  mounted() {},
};
</script>

<style lang="less" module>
.container {
  .content {
    width: 225px;
    height: 78px;
    line-height: 24px;
    font-size: 14px;
    text-align: center;
    border: 0.5px solid #868b8f;
    border-radius: 10px;
    background-color: #fff;
    opacity: 0.8;
    overflow: hidden;
    .titleName {
      color: #ffffff;
      background-color: #2574fb;
    }
    .value {
      padding: 0 8px;
      color: #444444;
      background-color: #ffffff;
    }
  }
}
</style>

3.3 挂载在地图上

思路:组件的挂载是在当你点击(拖动)图标时,触发事件,然后挂载组件并创建Overlay,其元素绑定弹框组件,之后添加到地图中。

3.3.1 Overlay

理解Overlay:顾名思义就是覆盖物、覆盖层的意思,也就是在地图之上再覆盖一层,用来显示一些元素。元素就是一般的HTML元素,利用 overlay,可以将可见元素放置到地图的任意位置,形成地图上再浮动一层的效果。例如在地图上相应的坐标放置一个标志,标签,利用 overlay 都可以做到,具体来说,点击地图上某位置,在点击位置弹出弹出框就是利用了 overlay。

初始化配置参数理解:
/**
 * Object literal with config options for the overlay.
 * @typedef {{id: (number|string|undefined),
 *     element: (Element|undefined),
 *     offset: (Array.<number>|undefined),
 *     position: (ol.Coordinate|undefined),
 *     positioning: (ol.OverlayPositioning|string|undefined),
 *     stopEvent: (boolean|undefined),
 *     insertFirst: (boolean|undefined),
 *     autoPan: (boolean|undefined),
 *     autoPanAnimation: (olx.animation.PanOptions|undefined),
 *     autoPanMargin: (number|undefined)}}
 * @api stable
 */
  • id:为对应的 overlay 设置一个 id,便于使用 ol.Map 的 getOverlayById 方法取得相应的 overlay;element,overlay 包含的 DOM element;
  • offset:偏移量,像素为单位,overlay 相对于放置位置(position)的偏移量,默认值是 [0, 0],正值分别向右和向下偏移;
  • position:在地图所在的坐标系框架下,overlay 放置的位置;
  • positioning:overlay 对于 position 的相对位置,可能的值包括
    • bottom-left、bottom-center、bottom-right 、center-left、center-center、center-right、top-left、top-center、top-right,默认是 top-left,也就是 element 左上角与 position 重合;
  • stopEvent:地图的事件传播是否停止,默认是 true,即阻止传播。例如:当鼠标滚轮在地图上滚动时,会触发地图缩放事件,如果在 overlay 之上滚动滚轮,并不会触发缩放事件,如果想鼠标在 overlay 之上也支持缩放,那么将该属性设置为 false 即可;
  • insertFirst:overlay 是否应该先添加到其所在的容器(container),当 stopEvent 设置为 true 时,overlay 和 openlayers 的控件(controls)是放于一个容器的,此时将 insertFirst 设置为 true ,overlay 会首先添加到容器,这样,overlay 默认在控件的下一层(CSS z-index),所以,当 stopEvent 和 insertFirst 都采用默认值时,overlay 默认在 控件的下一层;
  • autoPan:当触发 overlay setPosition 方法时触发,当 overlay 超出地图边界时,地图自动移动,以保证 overlay 全部可见;
  • autoPanAnimation:设置 autoPan 的效果动画,参数类型是 olx.animation.panOptions;
  • autoPanMargin:地图自动平移时,地图边缘与 overlay 的留白(空隙),单位是像素,默认是 20像素;
事件
  • change,当引用计数器增加时,触发;
  • change:element,overlay 对应的 element 变化时触发;
  • change:map,overlay 对应的 map 变化时触发;
  • change:offset,overlay 对应的 offset 变化时触发;
  • change:position,overlay 对应的 position 变化时触发;
  • change:positioning,overlay 对应的 positioning 变化时触发;
  • propertychange,overlay 对应的属性变化时触发;

overlay 的位置变化时在浏览器的控制台输出字符串的例子。

// example overlay event binding
var overlay = new ol.Overlay({
    // ...
});
overlay.on("change:position", function(){
    console.log("位置改变!");

方法
  支持的方法这里我们只介绍 overlay 特有的方法,就不介绍其继承而来的方法了,主要是针对 overlay 的属性及其相关联对象的 get 和 set 方法。
  • getElement,取得包含 overlay 的 DOM 元素;
  • getId,取得 overlay 的 id;
  • getMap,获取与 overlay 关联的 map对象;
  • getOffset,获取 offset 属性;
  • getPosition,获取 position 属性;
  • getPositioning,获取 positioning 属性;
  • setElement;设置 overlay 的 element;
  • setMap,设置与 overlay 的 map 对象;
  • setOffset,设置 offset;
  • setPosition,设置 position 属性;
  • setPositioning,设置 positioning 属性。

3.3.2 挂载组件并渲染到地图上

将要挂载的组件和Overlay的包导入到地图组件中,然后在事件中进行挂载:

//显示一些自定义元素,可以将DOM元素在地图上进行显示,并将随地图一起移动。
import Overlay from "ol/Overlay";
import MessageTip from "./map/MessageTip.vue";

        const MessageTipBox = Vue.extend(MessageTip);
        const messageTip = new MessageTipBox({
          propsData: {
            data: {
              title: "当前经纬度",
              longitude: e.coordinate[0],
              latitude: e.coordinate[1],
            },
          },
        });
        messageTip.$mount();

创建Overlay对象,并添加到地图中

        const overlay = new Overlay({
          positioning: "bottom-left",
          position: [e.coordinate[0], e.coordinate[1]], //定位
          element: messageTip.$el, //弹框的容器
          stopEvent: false,
          insertFirst: false,
          autoPan: true, //是否自动平移,即假如标记在屏幕边缘,弹出时自动平移地图使弹出框完全可见
          offset: [-110, -18],
          autoPanMargin: 0,
        });
        this.map.addOverlay(overlay);

点击弹出框的“X”图标时,实现关闭弹框。在弹框组件中传递了方法on-close

        messageTip.$on("on-close", () => {
          overlay.setPosition(undefined);
          this.map.removeOverlay(overlay);
        });

整个代码如下所示:

<template>
<div>
  <div class="ol-map" ref="olMap"></div>
  </div>
</template>

<script>
  import Vue from "vue";
  import "ol/ol.css";
  import { Map, View } from "ol";
  import { Tile as TileLayer } from "ol/layer";
  //显示鼠标经纬度
  // import { defaults as defaultControls } from "ol/control";
  import { defaults, FullScreen, MousePosition, ScaleLine } from "ol/control";
  import { XYZ } from "ol/source";
  // import { XYZ, OSM } from "ol/source";
  // fromLonLat方法能将坐标从经度/纬度转换为其他投影
  import { fromLonLat, transform } from "ol/proj";
  // 显示一个小icon图片
  import { vector, translate } from "../assets/js/feature.js";
  //显示一些自定义元素,可以将DOM元素在地图上进行显示,并将随地图一起移动。
  import Overlay from "ol/Overlay";
  import MessageTip from "./map/MessageTip.vue";
  export default {
    data() {
      return {
        map: null,
        flage: false,
        form: {
          longitude: null, //经度
          latitude: null, //纬度
        },
      };
    },
    components: {},
    computed: {},
    methods: {
      initMap() {
        this.map = new Map({
          // 设置地图图层
          layers: [this.getBaseLayer("vec_w"), this.getMarkerLayer("cva_w")], //地图源的瓦片图层
          // 设置显示地图的视图
          view: new View({
            center: fromLonLat(
              [120.18928024510497, 30.19961553664213],
              "EPSG:4326"
            ), //地图中心点 经纬度
            zoom: 19, // 缩放级别-显示层级
            minZoom: 0, // 最小缩放级别
            maxZoom: 18, // 最大缩放级别
            projection: "EPSG:4326",
            constrainResolution: true, // 因为存在非整数的缩放级别,所以设置该参数为true来让每次缩放结束后自动缩放到距离最近的一个整数级别,这个必须要设置,当缩放在非整数级别时地图会糊
          }),
          //olMap为map的地图容器
          target: this.$refs.olMap, // DOM容器
          //控制层
          controls: defaults().extend([
            new FullScreen(), // 全屏
            new MousePosition(), // 显示鼠标当前位置的经纬度
            new ScaleLine(), // 显示比例尺
          ]),
        });
        //显示一个图片icon
        this.map.addLayer(vector);
        // start 支持能拖动要素来修改它的位置
        this.map.addInteraction(translate);
        // 可以监听一下拖动开始和结束的事件,拖动后的经纬度可以从e里面获取
        translate.on("translateend", (e) => {
          if (this.flage) {
            const allOverlay = this.map.getOverlays();
            this.map.removeOverlay(allOverlay.array_[0]);
            allOverlay.array_.length = 0;
            console.log(allOverlay);
          } else {
            console.log("1");
          }
          this.flage = true;
          const MessageTipBox = Vue.extend(MessageTip);
          const messageTip = new MessageTipBox({
            propsData: {
              data: {
                title: "当前经纬度",
                longitude: e.coordinate[0],
                latitude: e.coordinate[1],
              },
            },
          });
          messageTip.$mount();
          const overlay = new Overlay({
            positioning: "bottom-left",
            position: [e.coordinate[0], e.coordinate[1]], //定位
            element: messageTip.$el, //弹框的容器
            stopEvent: false,
            insertFirst: false,
            autoPan: true, //是否自动平移,即假如标记在屏幕边缘,弹出时自动平移地图使弹出框完全可见
            offset: [-110, -18],
            autoPanMargin: 0,
          });
          this.map.addOverlay(overlay);
          messageTip.$on("on-close", () => {
            overlay.setPosition(undefined);
            this.map.removeOverlay(overlay);
          });
          console.log(e);
        });
        translate.on("translatestart", (e) => {});
      },
      // 天地图的瓦片资源
      //获取【地图底图】方法
      getBaseLayer(layer) {
        return new TileLayer({
          source: new XYZ({
            url: `http://t2.tianditu.com/DataServer?T=${layer}&x={x}&y={y}&l={z}&tk=075fbb86f6f79ca1be0739988c6a8d9c`,
          }),
        });
      },
      //获取【地图标记】方法
      getMarkerLayer(layer) {
        return new TileLayer({
          source: new XYZ({
            url: `http://t2.tianditu.com/DataServer?T=${layer}&x={x}&y={y}&l={z}&tk=075fbb86f6f79ca1be0739988c6a8d9c`,
          }),
        });
      },
    },
    mounted() {
      this.initMap();
    },
  };
</script>

<style lang="less" scoped>
  .ol-map {
    margin-top: 10px;
    width: 100%;
    height: 600px;
  }
  .search {
    margin: 5px;
    width: 250px;
    .el-input {
      width: 80%;
      margin-top: 5px;
    }
  }
</style>

最后即可实现如下效果:
1648696939079_.gif

  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值