这个例子讲解了如何通过单选Select和框选DragBox地块实现地块高亮效果。
绘制的时候记得按住ctrl键
首先初始化一个Map
initMap() {
this.map = new Map({
layers: [],
target: "map",
view: new View({
center: [0, 0],
zoom: 2,
constrainRotation: 16,
// rotation: Math.PI / 4 // 设置初始旋转角度45度
}),
});
},
然后加载一个矢量数据源,
const vectorSource = new VectorSource({
url: "https://openlayers.org/data/vector/ecoregions.json",
format: new GeoJSON(),
});
官方提供了一个数据源https://openlayers.org/data/vector/ecoregions.json
我们可以看一下它的结构,是一个GeoJSON格式的数据,里面包含了各个地块的feature,其中
properties里面是它的自定义的一些属性,后面会用到。
每个地块的样式是根据每个feature上的COLOR_BIO属性设置的,实例化图层的时候style后面写一个函数,函数的参数里可以返回每个feature,通过feature.get("COLOR_BIO")方法获取到每个颜色,再使用style.getFill().setColor(color);把颜色设置上去若是图层事先已经加载上去了使用this.regionLayer.setStyle()也可以实现同样的效果
addVectorLayer() {
const vectorSource = new VectorSource({
url: "https://openlayers.org/data/vector/ecoregions.json",
format: new GeoJSON(),
});
const style = new Style({
fill: new Fill({
color: "red",
}),
});
this.regionLayer = new VectorLayer({
source: vectorSource,
// background: "#1a2b39",
style: function (feature) {
const color = feature.get("COLOR_BIO") || "#eeeeee";
style.getFill().setColor(color);
return style;
},
});
this.map.addLayer(this.regionLayer);
},
接下来,添加选择器,select是在修改原有的feature的样式,所以这一步仍然需要动态设置每个feature的样式,这和利用点击事件拿取feature绘制新的高亮图层不同,使用点击事件绘制高亮效果是在原有的feature上面新建了一个图层新绘制了一个feature,设置feature的填充色位透明就可以刚好漏出来下面地块的颜色。用Select则需要再一次动态设置填充色。
const selectedStyle = new Style({
fill: new Fill({
color: "transparent",
}),
stroke: new Stroke({
color: "rgba(255, 255, 255, 0.7)",
width: 2,
}),
});
this.select = new Select({
style: function (feature) {
const color = feature.get("COLOR_BIO") || "#eeeeee";
selectedStyle.getFill().setColor(color);
return selectedStyle;
},
});
this.map.addInteraction(this.select);
获取 this.select上的所有feature,监听一个事件,方便把选中的feature上的信息实时显示到页面上。
const selectedFeatures = this.select.getFeatures();
selectedFeatures.on(["add", "remove"], ()=> {
const names = selectedFeatures.getArray().map((feature) => {
return feature.get("ECO_NAME");
});
if (names.length > 0) {
this.infoBox = names.join(", ");
} else {
this.infoBox = "None";
}
});
然后是DragBox,platformModifierKeyOnly的意思是需要按住Ctrl键或Command键才可以画出矩形框。
// platformModifierKeyOnly,这意味着只有在按下平台修饰键(例如Ctrl键或Command键)时才能拖拽出矩形框。
const dragBox = new DragBox({
condition: platformModifierKeyOnly,
});
let selectedFeatures = this.select.getFeatures();
this.map.addInteraction(dragBox);
监听一下开始绘制事件,每次绘制之前先清除页面上上一次绘制的矩形框
dragBox.on("boxstart", () => {
selectedFeatures.clear();
});
然后是绘制结束事件,主要就是找出在绘制的矩形框范围内的地块,找出之后直接加到select中。同时,触发["add",]事件,将信息显示到页面上。
dragBox.on("boxend", () => {
//获取拖拽矩形框的边界范围
const boxExtent = dragBox.getGeometry().getExtent();
//获取地图视图的投影范围。
const worldExtent = this.map.getView().getProjection().getExtent();
// 计算地图投影范围的宽度
const worldWidth = getWidth(worldExtent);
//计算拖拽矩形框所跨越的投影世界块的起始索引和结束索引。
const startWorld = Math.floor(
(boxExtent[0] - worldExtent[0]) / worldWidth
);
const endWorld = Math.floor(
(boxExtent[2] - worldExtent[0]) / worldWidth
);
//遍历所有的地块
for (let world = startWorld; world <= endWorld; ++world) {
const left = Math.max(
boxExtent[0] - world * worldWidth,
worldExtent[0]
);
const right = Math.min(
boxExtent[2] - world * worldWidth,
worldExtent[2]
);
const extent = [left, boxExtent[1], right, boxExtent[3]];
//找出在当前绘制的矩形框范围内的地块
const boxFeatures = this.regionLayer
.getSource()
.getFeaturesInExtent(extent)
.filter(
(feature) =>
!selectedFeatures.getArray().includes(feature) &&
feature.getGeometry().intersectsExtent(extent)
);
//获取地图倾斜数据
const rotation = this.map.getView().getRotation();
const oblique = rotation % (Math.PI / 2) !== 0;
//当视图倾斜需要将要素的几何形状和矩形框的几何形状进行旋转,然后检查它们是否相交,如果相交则将要素添加到 selectedFeatures 数组中。
if (oblique) {
const anchor = [0, 0];
const geometry = dragBox.getGeometry().clone();
geometry.translate(-world * worldWidth, 0);
geometry.rotate(-rotation, anchor);
const extent = geometry.getExtent();
boxFeatures.forEach(function (feature) {
const geometry = feature.getGeometry().clone();
geometry.rotate(-rotation, anchor);
if (geometry.intersectsExtent(extent)) {
selectedFeatures.push(feature);
}
});
} else {
//如果视图未倾斜,则直接将框选范围内的要素添加到 selectedFeatures 数组中。
selectedFeatures.extend(boxFeatures);
}
}
});
完整代码:
<template>
<div class="box">
<h1>BoxSelection</h1>
<div id="map"></div>
<div>
Selected regions: <span id="info">{{ infoBox }}</span>
</div>
</div>
</template>
<script>
import GeoJSON from "ol/format/GeoJSON.js";
import Map from "ol/Map.js";
import VectorLayer from "ol/layer/Vector.js";
import VectorSource from "ol/source/Vector.js";
import View from "ol/View.js";
import { DragBox, Select } from "ol/interaction.js";
import { Fill, Stroke, Style } from "ol/style.js";
import { getWidth } from "ol/extent.js";
import { platformModifierKeyOnly } from "ol/events/condition.js";
export default {
name: "",
components: {},
data() {
return {
map: null,
regionLayer: null,
select: null,
infoBox: "",
};
},
computed: {},
created() {},
mounted() {
this.initMap();
this.addVectorLayer();
this.addSelect();
this.addDragBox();
},
methods: {
initMap() {
this.map = new Map({
layers: [],
target: "map",
view: new View({
center: [0, 0],
zoom: 2,
constrainRotation: 16,
// rotation: Math.PI / 4 // 设置初始旋转角度45度
}),
});
},
addVectorLayer() {
const vectorSource = new VectorSource({
url: "https://openlayers.org/data/vector/ecoregions.json",
format: new GeoJSON(),
});
const style = new Style({
fill: new Fill({
color: "red",
}),
});
this.regionLayer = new VectorLayer({
source: vectorSource,
// background: "#1a2b39",
style: function (feature) {
const color = feature.get("COLOR_BIO") || "#eeeeee";
style.getFill().setColor(color);
return style;
},
});
this.map.addLayer(this.regionLayer);
},
addSelect() {
const selectedStyle = new Style({
fill: new Fill({
color: "transparent",
}),
stroke: new Stroke({
color: "rgba(255, 255, 255, 0.7)",
width: 2,
}),
});
this.select = new Select({
style: function (feature) {
const color = feature.get("COLOR_BIO") || "#eeeeee";
selectedStyle.getFill().setColor(color);
return selectedStyle;
},
});
this.map.addInteraction(this.select);
const selectedFeatures = this.select.getFeatures();
selectedFeatures.on(["add", "remove"], () => {
const names = selectedFeatures.getArray().map((feature) => {
return feature.get("ECO_NAME");
});
if (names.length > 0) {
this.infoBox = names.join(", ");
} else {
this.infoBox = "None";
}
});
},
addDragBox() {
const dragBox = new DragBox({
condition: platformModifierKeyOnly,
});
let selectedFeatures = this.select.getFeatures();
this.map.addInteraction(dragBox);
dragBox.on("boxstart", () => {
selectedFeatures.clear();
});
dragBox.on("boxend", () => {
//获取拖拽矩形框的边界范围
const boxExtent = dragBox.getGeometry().getExtent();
//获取地图视图的投影范围。
const worldExtent = this.map.getView().getProjection().getExtent();
// 计算地图投影范围的宽度
const worldWidth = getWidth(worldExtent);
//计算拖拽矩形框所跨越的投影世界块的起始索引和结束索引。
const startWorld = Math.floor(
(boxExtent[0] - worldExtent[0]) / worldWidth
);
const endWorld = Math.floor(
(boxExtent[2] - worldExtent[0]) / worldWidth
);
//遍历所有的地块
for (let world = startWorld; world <= endWorld; ++world) {
const left = Math.max(
boxExtent[0] - world * worldWidth,
worldExtent[0]
);
const right = Math.min(
boxExtent[2] - world * worldWidth,
worldExtent[2]
);
const extent = [left, boxExtent[1], right, boxExtent[3]];
//找出在当前绘制的矩形框范围内的地块
const boxFeatures = this.regionLayer
.getSource()
.getFeaturesInExtent(extent)
.filter(
(feature) =>
!selectedFeatures.getArray().includes(feature) &&
feature.getGeometry().intersectsExtent(extent)
);
//获取地图倾斜数据
const rotation = this.map.getView().getRotation();
const oblique = rotation % (Math.PI / 2) !== 0;
//当视图倾斜需要将要素的几何形状和矩形框的几何形状进行旋转,然后检查它们是否相交,如果相交则将要素添加到 selectedFeatures 数组中。
if (oblique) {
const anchor = [0, 0];
const geometry = dragBox.getGeometry().clone();
geometry.translate(-world * worldWidth, 0);
geometry.rotate(-rotation, anchor);
const extent = geometry.getExtent();
boxFeatures.forEach(function (feature) {
const geometry = feature.getGeometry().clone();
geometry.rotate(-rotation, anchor);
if (geometry.intersectsExtent(extent)) {
selectedFeatures.push(feature);
}
});
} else {
//如果视图未倾斜,则直接将框选范围内的要素添加到 selectedFeatures 数组中。
selectedFeatures.extend(boxFeatures);
}
}
});
},
},
};
</script>
<style lang="scss" scoped>
#map {
width: 100%;
height: 500px;
}
.box {
height: 100%;
}
</style>