本文采用天地图的瓦片资源,结合openlayers进行展示。【浏览器端】
技术:vue+ol
1 注册成为开发者
天地图官网:http://lbs.tianditu.gov.cn/home.html
注册为开发者后,创建应用获取key值。本文采用【浏览器端】端key值。
2 使用天地图的瓦片资源
在地图API中,根据需要自行使用。
由图可以看出天地图的地图是由两部分组成的:【底图】、【标记】,所以当我们引用时,我们需要将底图以及对应的标记结合一起使用。
瓦片资源使用例子:
使用【矢量底图】: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_c | cva_c | 经纬度投影 |
vec_w | cva_w | 球面墨卡托投影 | |
影像 | img_c | cia_c | 经纬度投影 |
img_w | cia_w | 球面墨卡托投影 | |
地形 | ter_c | cta_c | 经纬度投影 |
ter_w | cta_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 弹框功能
前期的基础学习和了解后,我们来实现一个弹窗功能吧~~~
效果实现:移动地图上的定位icon,弹框也会随着移动并且经纬度也会相应的更新,点击关闭按钮弹框关闭,再次点击图标会再次出现。
大概的实现思路:
在地图上显示标记,也就是在瓦片地图层上新叠加了一层,也就是矢量层;弹窗也是,都是图层的叠加,跟PS的图层效果一样,所以我们要创建矢量层,并且添加到map中就可以了。
- 矢量图层中引入图标,然后添加到map中。
- 写弹框组件
- 挂载在地图上!!
- 实现每次移动图标弹框也会随着一起移动。
理清思路之后让我们看看如何实现吧!
3.1 在地图上显示图标
准备: 首先是找一个图标icon,这里我是在阿里图标里面下载的一个png图片。
具体的实现思路:
- 创建矢量对象
Feature
,设置样式里创建Icon对象,然后将矢量对象放在矢量源VectorSource
里,再把矢量源放到矢量图层VectorLayer
里,最后将矢量图层【定位图标】添加到地图。 - 可以拖动效果:创建
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 支持能拖动要素来修改它的位置
实现效果:
3.2 创建弹窗组件
效果:
根据图片,写组件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>
最后即可实现如下效果: