效果展示
HTML结构和样式
<template>
<div class="page_show">
<div id="showPage" class="showPage-demo">
<div id="popup" class="map-popup" ref="popup">
<div class="popup-closed" @click="closed">×</div>
<div class="map-popup-content" ref="popupContent">
<div>设备编号:{{ popupData.code }}</div>
<div>设备地址:{{ popupData.addr }}</div>
<div>设备类型:{{ popupData.name }}</div>
</div>
</div>
</div>
<filtrate
class="page-filtrate"
:filterData="filterData"
@type="lengenChange"
@equipment="changEquipment"
></filtrate>
<el-button class="page_show_Polygon" type="primary" @click="areaSreening">
区域筛选
</el-button>
<el-button
class="page_show_Polygon_cancel"
type="danger"
@click="cancelArea"
>
取消区域
</el-button>
<map-legend :list="exampleList" class="page_map_Legend"></map-legend>
<div class="page_show_slect">
<el-select
v-model="searchFu"
placeholder="请选择"
filterable
style="width: 400px"
>
<el-option
v-for="item in adrAAray"
:key="item.feature.id_"
:label="item.addr"
:value="item.feature.id_"
>
</el-option>
</el-select>
</div>
</div>
</template>
<style>
.page-filtrate {
position: absolute;
top: 10px;
right: 0;
}
.page_show_Polygon {
position: absolute;
top: 50px;
right: 0;
}
.page_show_Polygon_cancel {
position: absolute;
top: 90px;
right: 0;
}
.page_show_slect {
position: absolute;
top: 20px;
left: 20px;
}
.page_map_Legend {
position: absolute;
bottom: 20px;
left: 0;
}
.page_show,
.showPage-demo {
width: 100%;
height: 100%;
position: relative;
top: 0;
left: 0;
}
/* popup */
.map-popup {
position: absolute;
background-color: #fff;
filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
padding: 10px;
border-radius: 10px;
border: 1px solid #ccc;
left: 0px;
bottom: 0px;
}
.map-popup ::after {
top: 100%;
border: solid transparent;
content: "";
width: 0;
height: 0;
position: absolute;
pointer-events: none;
}
.map-popup ::after {
border-top-color: #fff;
border-width: 10px;
left: 50%;
bottom: 0;
transform: translate(-50%, 0);
}
.popup-closed {
position: absolute;
top: 0px;
right: 0px;
width: 20px;
height: 20px;
background: #000;
color: #fff;
text-align: center;
line-height: 20px;
font-size: 16px;
border-radius: 20px 0 20px 20px;
cursor: pointer;
}
.map-popup-content {
line-height: 24px;
font-size: 12px;
color: #2b2b2b;
width: 300px;
}
</style>
公共方法和全局图层
数据来源自己模拟数据
import mark from "@/assets/mark.png";
import Feature from "ol/Feature";
import { Point } from "ol/geom";
import { Vector as VectorSource } from "ol/source";
import { Vector as VectorLayer } from "ol/layer";
import {
Fill,
Stroke,
Style,
Icon,
Text,
Circle as CircleStyle,
} from "ol/style";
import { GeoJSON } from "ol/format";
import { initMap, devList } from "./district"; //或者区域
import config from "./globalConfig.json";
import { Select, Modify, Draw } from "ol/interaction";
import { altKeyOnly, doubleClick } from "ol/events/condition";
import { getCenter } from "ol/extent";
//公共方法
const GAS_POINT_TYPE = () => {
const datas = config.GAS_POINT_TYPE.children;
datas.map((item) => (item.type = item.val.typeId.value));
return datas;
};
const LINE_MATERIAL_ARRAY = () => {
let array = [];
config.GAS_LINE_MATERIAL.children.map((item) => {
array.push({
code: item.code,
name: item.name,
strokeColor: item.val.color.value,
strokeWidth: item.val.width.value,
material: item.val.material.value,
});
});
return array;
};
const LINE_PRESSURE_ARRAY = () => {
let array = [];
config.GAS_LINE_PRESSURE_LEVEL.children.map((item) => {
array.push({
code: item.code,
name: item.name,
strokeColor: item.val.color.value,
strokeWidth: item.val.width.value,
pressureType: item.val.pressureType.value,
});
});
return array;
};
const LINE_BELONGING_ARRAY = () => {
let array = [];
config.ENGINEERING_OF_BELONGING.children.map((item) => {
array.push({
code: item.code,
name: item.name,
strokeColor: item.val.color.value,
strokeWidth: item.val.width.value,
belong: item.val.belong.value,
});
});
return array;
};
const toResolution = (resolution) => {
return 1 / (((resolution * 50) / 2.54) * 100) > 1
? 1
: 1 / (((resolution * 50) / 2.54) * 100);
};
const analysisLayer = function (layer, callBack) {
layer
.getSource()
.getFeatures()
.forEach((featrue) => {
callBack && callBack(featrue);
});
};
function gasPointType() {
let customPoint = config.CUSTOM_POINT.val,
GAS_POINT_TYPE = config.GAS_POINT_TYPE.children;
GAS_POINT_TYPE.forEach((item) => {
item.type = item.val.typeId.value;
});
return [
...GAS_POINT_TYPE,
{ type: customPoint.typeId.value, val: customPoint },
];
}
//中心标志点
function styleFunction(featrue, resolution) {
return new Style({
image: new Icon({
src: mark,
scale: 1,
}),
text: new Text({
textAlign: "center",
textBaseline: "middle",
font: "normal 14px 微软雅黑",
text: initMap.district.properties.name,
fill: new Fill({ color: "#aa3300" }),
stroke: new Stroke({ color: "#ffcc33", width: 2 }),
offsetY: 0,
scale: 1,
offsetY: -20,
}),
});
}
const centerPoint = initMap.centerPoint.geometry.coordinates.slice(0, 2);
const centerMark = new VectorLayer({
style: styleFunction,
source: new VectorSource({
features: [
new Feature({
geometryName: "centerMark",
geometry: new Point(centerPoint),
}),
],
}),
});
//地区
const areaGeom = new GeoJSON({
geometryName: "area",
}).readFeatures(initMap.district.geometry);
const areaLayer = new VectorLayer({
source: new VectorSource({
features: areaGeom,
}),
style: new Style({
stroke: new Stroke({
color: "rgba(255, 0,0, 1)",
lineDash: [4],
width: 2,
}),
fill: null,
text: new Text({
text: initMap.district.properties.name,
textAlign: "center",
font: "14px PingFangSC",
padding: [3, 5, 3, 5],
overflow: true,
fill: new Fill({
color: "yellow",
}),
backgroundFill: new Fill({
color: "#48B5FA",
}),
}),
}),
});
//设备
let equipmentFeature = new GeoJSON({
geometryName: "equipment",
}).readFeatures({
type: devList.type,
features: devList.features,
});
const equipmentLayer = new VectorLayer({
source: new VectorSource({
features: equipmentFeature,
}),
//管道默认为压力展示
style: (featrue, resolution) => styleFu(featrue, resolution, "pressureType"),
});
const lineEqimentStyles = {
material: (properties, resolution) => {
let confiLine = LINE_MATERIAL_ARRAY().find(
(item) => item.material === properties.material || "1"
);
return new Style({
stroke: new Stroke({
color: confiLine.strokeColor,
width: confiLine.strokeWidth * toResolution(resolution),
}),
text: new Text({
scale: toResolution(resolution),
text: confiLine.name,
placement: "line",
textAlign: "center",
font: `${toResolution(resolution) * 14}px PingFangSC bloder`,
padding: [2, 2, 2, 2],
overflow: true,
fill: new Fill({
color: "#000",
}),
}),
});
}, //材料
pressureType: (properties, resolution) => {
let confiLine = LINE_PRESSURE_ARRAY().find(
(item) => item.pressureType === properties.pressureType || "1"
);
// console.log(confiLine, "result");
return new Style({
stroke: new Stroke({
color: confiLine.strokeColor,
width: confiLine.strokeWidth * toResolution(resolution),
}),
text: new Text({
scale: toResolution(resolution),
text: confiLine.name,
placement: "line",
textAlign: "center",
font: `${toResolution(resolution) * 14}px PingFangSC bloder`,
padding: [2, 2, 2, 2],
overflow: true,
fill: new Fill({
color: "#000",
}),
}),
});
}, //压力
belong: (properties, resolution) => {
let confiLine = LINE_BELONGING_ARRAY().find(
(item) => item.belong === properties.belong || "1"
);
return new Style({
stroke: new Stroke({
color: confiLine.strokeColor,
width: confiLine.strokeWidth * toResolution(resolution),
}),
text: new Text({
scale: toResolution(resolution),
text: confiLine.name,
placement: "line",
textAlign: "center",
font: `${toResolution(resolution) * 14}px PingFangSC bloder`,
padding: [2, 2, 2, 2],
overflow: true,
fill: new Fill({
color: "#000",
}),
}),
});
}, //工程
};
function getICon(properties) {
let typeId = properties.typeId || "1",
allPoint = gasPointType();
return allPoint.find((i) => i.type == typeId).val;
}
function styleFu(featrue, resolution, lineStyleType) {
let type = featrue.getGeometry().getType(),
properties = featrue.getProperties(),
scale = toResolution(resolution);
if (type === "Point") {
//各点展示
let item = getICon(properties);
return new Style({
image: new Icon({
rotation: 0,
src: item.iconUrl.value,
anchorXUnits: "fraction",
// anchorYUnits: 'pixels',
imgSize: [item.width.value, item.height.value],
scale,
}),
});
}
if (type === "LineString") {
return lineEqimentStyles[lineStyleType](properties, resolution);
}
}
function bounce(t) {
const s = 7.5625;
const p = 2.75;
let l;
if (t < 1 / p) {
l = s * t * t;
} else {
if (t < 2 / p) {
t -= 1.5 / p;
l = s * t * t + 0.75;
} else {
if (t < 2.5 / p) {
t -= 2.25 / p;
l = s * t * t + 0.9375;
} else {
t -= 2.625 / p;
l = s * t * t + 0.984375;
}
}
}
return l;
}
function searchfeatrueId(newVal, vm) {
let souce = equipmentLayer.getSource(),
featrue = souce.getFeatureById(newVal),
geom = featrue.getGeometry(),
coordinates = geom.getCoordinates(),
type = featrue.getGeometry().getType(),
center;
vm.popupData = featrue.getProperties();
switch (type) {
case "Point":
center = coordinates.slice(0, 2);
break;
case "LineString":
center = getCenter(geom.getExtent());
break;
}
console.log(vm.map.getView());
vm.map.getView().setCenter(center);
// vm.map.getView().animate({
// center,
// duration: 2000,
// zoom: 12,
// });
vm.popupOverlay.setPosition(center);
}
//编辑功能
const selected = new Style({
fill: new Fill({
color: "#444",
}),
stroke: new Stroke({
color: "rgba(255, 0, 255, 0.9)",
width: 2,
}),
});
const select = new Select({
condition: function (mapBrowserEvent) {
return doubleClick(mapBrowserEvent) && altKeyOnly(mapBrowserEvent);
},
style: selected,
layers: [equipmentLayer],
// filter: (feature) => { //过滤那些不额能编辑
// let properties = feature.getProperties();
// if (properties.highlight == 1) {
// return true;
// } else {
// Message({
// message: "不能编辑哦",
// center: true,
// });
// }
// },
});
const modify = new Modify({
features: select.getFeatures(),
});
modify.on("modifyend", () => {
select.getFeatures().clear();
});
//绘制多边形
const polygonSource = new VectorSource({
wrapX: true,
});
const vector = new VectorLayer({
source: polygonSource,
style: new Style({
fill: new Fill({ color: "rgba(0,0,0, 0.2)" }),
stroke: new Stroke({ color: "#0ff", lineDash: [4], width: 2 }),
//image: new CircleStyle({ radius: 7, fill: new Fill({ color: "#f00" }) }),
}),
});
const draw = new Draw({
source: polygonSource,
type: "Polygon",
});
draw.setActive(false);
export {
centerMark,
centerPoint,
areaLayer,
equipmentLayer,
LINE_PRESSURE_ARRAY,
LINE_MATERIAL_ARRAY,
LINE_BELONGING_ARRAY,
GAS_POINT_TYPE,
styleFu,
lineEqimentStyles,
modify,
select,
searchfeatrueId,
bounce,
draw,
vector,
polygonSource,
analysisLayer,
areaGeom,
};
filtrate 组件
<template>
<el-popover placement="left" width="300" trigger="click">
<el-form label-position="top" label-width="40px">
<el-form-item label="颜色显示">
<el-radio-group v-model="filterData.type" @change="typeChange">
<el-radio label="pressureType">压力级别</el-radio>
<el-radio label="material">材质</el-radio>
<el-radio label="belong">工程归属</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="设备显示">
<el-tree
:data="typeData"
show-checkbox
default-expand-all
:default-checked-keys="filterData.equipment"
node-key="type"
ref="tree"
highlight-current
:props="defaultProps"
@check="handleChecke"
></el-tree>
</el-form-item>
</el-form>
<template slot="reference">
<el-button type="success">高级筛选</el-button>
</template>
</el-popover>
</template>
<script>
import { GAS_POINT_TYPE } from "@/utils/showPage.js";
export default {
name: "filtrate",
props: {
filterData: {
type: Object,
required: true,
default: () => {
return {};
},
},
},
data() {
return {
typeData: [
{
name: "设备",
type: "all",
children: [
{
type: "0",
name: "燃气管道",
},
...GAS_POINT_TYPE(),
],
},
],
defaultProps: {
children: "children",
label: "name",
},
};
},
methods: {
handleChecke(
node,
{ checkedKeys, checkedNodes, halfCheckedKeys, halfCheckedNodes }
) {
this.$emit("equipment", checkedKeys);
},
typeChange(val) {
this.$emit("type", val);
},
},
mounted() {},
};
</script>
mapLegend 组件
<template>
<div class="map-legend" functional>
<div v-for="(item, index) in list" :key="`mapLegend${index}`" class="map-legend-item">
<div class="legen_color" :style="{ background: item.strokeColor }"></div>
<div class="legen_name">{{ item.name }}</div>
</div>
</div>
</template>
<script>
export default {
name: 'mapLegend',
props: {
list: {
required: true,
type: Array,
default: () => {}
}
}
};
</script>
<style scoped>
.map-legend {
background: rgba(255, 255, 255, 0.8);
padding: 5px;
}
.map-legend-item {
display: flex;
padding: 0 6px;
}
.map-legend-item .legen_color {
width: 40px;
height: 10px;
margin: 5px 0;
}
.map-legend-item .legen_name {
padding-left: 4px;
line-height: 20px;
font-size: 12px;
}
</style>
VUE data初始化值
data() {
return {
searchFu: "",
map: null,
filterData: {
type: "pressureType",
equipment: ["all"],
},
exampleList: [],
adrAAray: [],
popupOverlay: null,
popupData: {},
};
},
VUE created 生命周期/加载mapMapLegend数据、下拉筛选栏
created() {
let { type } = this.filterData;
this.getMapLengebd(type);
let Features = equipmentLayer.getSource().getFeatures();
this.initSlect(Features, true);
},
methods:{
getMapLengebd(type) {
switch (type) {
case "pressureType":
this.exampleList = LINE_PRESSURE_ARRAY();
break;
case "material":
this.exampleList = LINE_MATERIAL_ARRAY();
break;
case "belong":
this.exampleList = LINE_BELONGING_ARRAY();
break;
}
},
initSlect(Features, initialRender) {
let _this = this,
adrAAray = [];
Features.forEach((feature) => {
if (initialRender) {
feature.setProperties({
isshowEquipment: true,
isshowScope: true,
});
}
adrAAray.push({
addr:
feature.getProperties().addr + `(${feature.getProperties().name})`,
feature: feature,
});
});
_this.adrAAray = adrAAray;
},
}
VUE mounted生命周期 初始地图和地图监听
mounted() {
this.initMap();
},
methods:{
initMap() {
let map = new Map({
// 设置地图图层
layers: [gaodeMapLayer, areaLayer, centerMark, vector, equipmentLayer],
// 设置显示地图的视图
view: new View({
center: centerPoint,
zoom: 11,
minZoom: 2,
maxZoom: 18,
projection: "EPSG:4326",
rotation: 0,
}),
// 让id为map的div作为地图的容器
target: "showPage",
interactions: defaultInteractions({
doubleClickZoom: false, // 取消双击放大功能交互
// mouseWheelZoom: false, // 取消滚动鼠标中间的滑轮交互
// shiftDragZoom: false, // 取消shift+wheel左键拖动交互
}).extend([select, modify, draw]),
});
this.map = map;
this.addEventListenerMAP(map);//监听变化
this.initPopup(map);//初始化popup
this.addEventListenerDraw();//监听Draw
},
addEventListenerMAP(map) {
let _this = this,
{ popupOverlay } = _this;
map.on("pointermove", (e) => {
let pixel = map.getEventPixel(e.originalEvent);
let hit = map.hasFeatureAtPixel(pixel);
map.getTargetElement().style.cursor = hit ? "pointer" : "";
});
map.on("singleclick", (evt) => {
map.forEachFeatureAtPixel(evt.pixel, (feature, layer) => {
let type = feature.getGeometry().getType(),
properties = feature.getProperties();
if (feature.getGeometryName() === "equipment") {
this.popupData = properties;
this.popupOverlay.setPosition(evt.coordinate);
}
});
});
},
initPopup(map) {
let { popup } = this.$refs,
_this = this;
let popupOverlay = new Overlay({
element: popup,
autoPan: true,
positioning: "center-center",
stopEvent: true,
offset: [-160, -10],
autoPanAnimation: {
duration: 3000,
easing: bounce,
},
});
popupOverlay.setPosition(undefined);
// console.log(popupOverlay)
this.popupOverlay = popupOverlay;
map.addOverlay(popupOverlay);
},
addEventListenerDraw() {
let _this = this;
draw.on("drawend", (evt) => {
let polygon = evt.feature.getGeometry(),
Features = [],
{ type, equipment } = _this.filterData;
analysisLayer(equipmentLayer, (featrue) => {
let coordinates = featrue.getGeometry().getCoordinates(),
featrueType = featrue.getGeometry().getType(),
sign = false,
properties = featrue.getProperties(),
{ isshowEquipment, isshowScope } = properties;
if (isshowEquipment) {
switch (featrueType) {
case "Point":
sign = polygon.intersectsCoordinate(coordinates);
break;
case "LineString":
sign =
polygon.intersectsCoordinate(coordinates[0]) &&
polygon.intersectsCoordinate(coordinates[1]);
break;
} //是否在这个区域
if (sign) {
featrue.setStyle((featrue, resolution) =>
styleFu(featrue, resolution, type)
);
properties["isshowScope"] = true;
Features.push(featrue);
} else {
properties["isshowScope"] = false;
featrue.setStyle(() => null);
}
featrue.setProperties(properties);
}
});
_this.initSlect(Features);
draw.setActive(false);
});
},
}
温馨提示
如有不明白的地方请留言! 可以一起探讨功能点 后续有更精彩的 开发中真实需求奉上