一、前言
大家好,这里分享一个 Vue3 + OpenLayers 的小案例:
模仿共享单车的电子围栏功能,用户在地图上绘制停泊点时,系统会自动判断该点是否在规划好的电子围栏内(多边形或圆形)。
这个功能在实际项目中有很大应用场景,比如:
共享单车/电动车:判断用户还车是否在规定区域;
物流调度:判断车辆停靠是否在任务范围内;
园区/景区管理:判断人员是否进入限制区域。
下面我们通过一个完整的 Vue3 + OpenLayers 示例来实现。
三、功能效果
二、环境依赖
Vue3 + Vite + TypeScript
OpenLayers (ol)
Element Plus(消息提示用)
安装依赖:
npm install ol element-plus
四、核心实现思路
初始化地图:加载 OSM 底图,添加一个点图层和一个围栏图层。
绘制电子围栏:提前设置一个多边形区域和一个圆形区域。
交互绘制点:通过
Draw
交互让用户选择停泊点。判断点是否在围栏内:利用
geometry.intersectsCoordinate(coord)
方法判断坐标是否落在围栏内。消息提示:使用
ElMessage
提示用户结果。
五、完整代码示例
<!--* @Author: 彭麒* @Date: 2025/9/4* @Email: 1062470959@qq.com* @Description: 此源码版权归吉檀迦俐所有,可供学习和借鉴或商用。-->
<template><div class="container"><div class="w-full flex justify-center flex-wrap"><div class="font-bold text-[24px]">vue3+openlayers: 模仿共享单车,判断点是否放在规划的电子围栏内</div></div><h4><el-button type="primary" size="small" @click="drawImage">绘制停泊点</el-button></h4><div id="vue-openlayers"></div></div>
</template><script setup lang="ts">
import "ol/ol.css";
import { onMounted, ref } from "vue";
import { Map, View } from "ol";
import Tile from "ol/layer/Tile";
import OSM from "ol/source/OSM";
import LayerVector from "ol/layer/Vector";
import SourceVector from "ol/source/Vector";
import Fill from "ol/style/Fill";
import Feature from "ol/Feature";
import { Point, Circle as OLCircle, Polygon } from "ol/geom";
import Stroke from "ol/style/Stroke";
import Style from "ol/style/Style";
import CircleStyle from "ol/style/Circle";
import Draw from "ol/interaction/Draw";
import { ElMessage } from "element-plus";// 地图对象
const map = ref<Map | null>(null);
const draw = ref<Draw | null>(null);// 点数据源
const source = new SourceVector({wrapX: false,
});// 围栏数据源
const dataSource = new SourceVector({wrapX: false,
});// 多边形数据
const polygonData = [[[116.005, 39.005],[115.006, 40.008],[112.008, 39.008],[116.005, 39.005],],
];// 圆形数据
const circleData = {circleCenter: [115.992, 38.5],circleRadius: 0.5,
};// 显示多边形
const showPolygon = () => {const polygonFeature = new Feature({geometry: new Polygon(polygonData),});dataSource.addFeature(polygonFeature);
};// 显示圆形
const showCircle = () => {const circleFeature = new Feature({geometry: new OLCircle(circleData.circleCenter, circleData.circleRadius),});dataSource.addFeature(circleFeature);
};// 初始化地图
const initMap = () => {const mapLayer = new Tile({source: new OSM(),});const pointLayer = new LayerVector({source: source,style: new Style({image: new CircleStyle({radius: 5,fill: new Fill({color: "#f0f",}),}),}),});const weilan = new LayerVector({source: dataSource,style: new Style({fill: new Fill({color: "transparent",}),stroke: new Stroke({width: 2,color: "red",}),}),});map.value = new Map({target: "vue-openlayers",layers: [mapLayer, weilan, pointLayer],view: new View({projection: "EPSG:4326",center: [115.006, 39.508],zoom: 8,}),});
};// 绘制停泊点
const drawImage = () => {if (!map.value) return;source.clear();// 停止上一次绘制if (draw.value !== null) {map.value.removeInteraction(draw.value);}draw.value = new Draw({source: source,type: "Point",});map.value.addInteraction(draw.value);draw.value.on("drawend", (e) => {if (!map.value) return;map.value.removeInteraction(draw.value as Draw);const coord = (e.feature.getGeometry() as Point).getCoordinates();const arr = dataSource.getFeatures();let flag = 0;for (let i = 0; i < arr.length; i++) {const polygonGeometry = arr[i].getGeometry();if (polygonGeometry?.intersectsCoordinate(coord)) {flag++;}}if (flag) {ElMessage.success({message: "在电子围栏内",duration: 1000,});} else {ElMessage.error({message: "在电子围栏外",duration: 1000,});}});
};onMounted(() => {initMap();showPolygon();showCircle();
});
</script><style scoped>
.container {width: 840px;height: 600px;margin: 50px auto;border: 1px solid #42b983;
}#vue-openlayers {width: 800px;height: 430px;margin: 0 auto;border: 1px solid #42b983;position: relative;
}
</style>
七、总结与扩展
本文实现了一个 基础电子围栏判断功能,在真实业务中可以扩展为:
支持多围栏:一个城市多个禁停区/允许停放区;
围栏编辑:后台管理系统可视化绘制/修改电子围栏;
轨迹回放:判断车辆行驶轨迹是否进入围栏;
性能优化:大规模电子围栏渲染(可用 GeoJSON 数据源)。