这篇主要讲的是如何根据地图上的矢量地块定位
首先,mounted里面加载一个底图到地图上。
const view = new View({
projection: "EPSG:4326",
center: [116.389, 39.903],
zoom: 7,
});
this.map = new Map({
layers: [
new TileLayer({
source: new XYZ({
url: "http://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png",
}),
})],
target: "map",
view: view,
});
接下来我们来加载矢量的地块了,官网示例里引入的是它自己写的一个switzerland.geojson文件
里面有多边形,圆形,下载源码可能可以找到这个json。咱懒得找,直接找个地区的地块替代一下就好。
打开这个网址,可以获取各大地区的geojson文件。它提供了几种访问方式,可以通过网址在线获取,也可以直接把文件下下来放到项目里引用。
咱们点到北京市,直接复制在线地址。
methods里面写一个方法addRegionLayer,定义一个矢量的source,把url写成我们刚刚复制的地址。110000表示的就是北京市,如果选择别的城市会发现编码不一样,其他都一样。
format
属性是一个实例化的 GeoJSON 类。它告诉 OpenLayers 如何解析加载的数据。在这里,通过 new GeoJSON()
创建了一个 GeoJSON 格式解析器,这样 OpenLayers 就知道加载的数据是以 GeoJSON 格式编码的。(简单理解:固定写法,source里只要写了url,format就得写。加载geojson数据类型的矢量数据源时format就是 new GeoJSON()
)
addRegionLayer() {
const source = new VectorSource({
url: "https://geo.datav.aliyun.com/areas_v3/bound/110000_full.json",
format: new GeoJSON(),
});
},
然后再把矢量的source放到矢量图层上,style里面是样式,stroke表示描边,fill表示填充。官网里写了一些circle圆形的样式,但是我们的地块都是多边形,所以不要circle相关的。style其实还有别的写法,这个咱们后面再说。
this.vectorLayer = new VectorLayer({
source: source,
style: {
"fill-color": "rgba(255, 255, 255, 0.6)",
"stroke-width": 2,
"stroke-color": "#319FD9",
},
});
然后,把图层加到地图上。使用addlayer这个api往地图上添加图层。也可以按照官网上的示例写法直接在new Map之前先把vectorLayer写好,写到layers数组中。我这里使用这个api主要是为了给大家展示一下这种写法,一般这种用的比较多,因为实际工作需求中有很多图层是后加的,比如点击事件之后加一个图层到地图上。
this.map.addLayer(this.vectorLayer)
就可以在地图上看到我们加载的图层了。
然后来写定位,写三个按钮,定义一个fitTo方法分别传想要定位的区域名称
<div class="fitBox">
<el-button type="primary" @click="fitTo('密云区')"
>定位至密云区</el-button
>
<el-button type="primary" @click="fitTo('门头沟区')"
>定位至门头沟区</el-button
>
<el-button type="primary" @click="fitTo('通州区')"
>定位至通州区</el-button
>
</div>
methods里面定义一个fitTo方法,先来打印一下获取的features到底是个什么东西
fitTo(type) {
const features = this.vectorLayer.getSource().getFeatures();
console.log("features", features);
},
一个数组里装了16个Feature类,数数北京市下面的地区刚好也是16个,由此可知,一个feature类就是一个地区多边形。
点开其中一个,可以看见很多属性,Feature其实就是一个描述地块属性的类,里面有很多属性代表不同的东西,可以理解为一个很大的对象。
在values里可以看到geometry,geometry表示是一个几何对象,描述了要素的几何形状,它的属性值一般有点的坐标、线的路径、面的边界等。在 OpenLayers 中,几何对象可以是 Point点、LineString线、Polygon多边形、MultiPolygon多个多边形 等类型。
有一个name属性是区域名称,我们的思路就是循环遍历所有features找到我们想要定位到的地区的feature。getGeometry()就是获取了feature的几何形状,再使用fit方法进行定位。padding
参数是一个长度为 4 的数组,用于指定在适合指定几何对象时,地图视图周围的填充量。数组的四个值分别表示上、右、下、左四个方向的填充量(以像素为单位)。
features.forEach((v) => {
if (v.get("name") == type) {
const polygon = v.getGeometry();
this.map.getView().fit(polygon, { padding: [170, 50, 30, 150] });
}
});
看下效果:
小细节1:为什么把vectorLayer提取为全局变量而不提view和source?
一般来说,一个项目中会加载很多图层,如果把每个图层的source、layer、每个地图的View都提为全局变量那太多了,容易看不清楚。所以一般把图层和Map提取出来就好,要用的时候使用api——this.layer.getSource()、this.map.getView()获取就好。
小细节2:地区的feature为什么是MultiPolygon而不是Polygon?
以北京的朝阳区为例,一个区是两块不相连的地块组成,这种情况下使用一个地区对应一个Polygon就不够了,因为一个多边形的起点和终点必须是闭合的。所以要用MultiPolygon
完整代码:
<template>
<div class="box">
<h1>Advanced Mapbox Vector Tiles</h1>
<div id="map"></div>
<div class="fitBox">
<el-button type="primary" @click="fitTo('密云区')"
>定位至密云区</el-button
>
<el-button type="primary" @click="fitTo('门头沟区')"
>定位至门头沟区</el-button
>
<el-button type="primary" @click="fitTo('通州区')"
>定位至通州区</el-button
>
</div>
</div>
</template>
<script>
import GeoJSON from "ol/format/GeoJSON.js";
import Map from "ol/Map.js";
import View from "ol/View.js";
import { OSM, Vector as VectorSource, XYZ } from "ol/source.js";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
export default {
name: "",
components: {},
data() {
return {
vectorLayer: null,
map: null,
};
},
computed: {},
created() {},
methods: {
fitTo(type) {
const features = this.vectorLayer.getSource().getFeatures();
console.log("features", features);
features.forEach((v) => {
if (v.get("name") == type) {
const polygon = v.getGeometry();
this.map.getView().fit(polygon, { padding: [170, 50, 30, 150] });
}
});
},
addRegionLayer() {
const source = new VectorSource({
url: "https://geo.datav.aliyun.com/areas_v3/bound/110000_full.json",
format: new GeoJSON(),
});
this.vectorLayer = new VectorLayer({
source: source,
style: {
"fill-color": "rgba(255, 255, 255, 0.6)",
"stroke-width": 2,
"stroke-color": "#319FD9",
},
});
this.map.addLayer(this.vectorLayer)
},
},
mounted() {
const view = new View({
projection: "EPSG:4326",
center: [116.389, 39.903],
zoom: 7,
});
this.map = new Map({
layers: [
new TileLayer({
source: new XYZ({
url: "http://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png",
}),
}),
],
target: "map",
view: view,
});
this.addRegionLayer()
},
};
</script>
<style lang="scss" scoped>
#map {
width: 100%;
height: 500px;
}
.box {
height: 100%;
}
</style>