目录
- 1.直接创建多边形(适用于加载)
- 2.手动绘制多边形
- 3.电子围栏的上传和下载
- 4.判断坐标是否在多边形内
1.直接创建 (内容合并到后面)
2.通过鼠标绘制围栏 (内容合并到后面)
3.电子围栏的上传和下载 (内容合并到后面)
4.判断坐标是否在多边形内
- 数据库设计
CREATE TABLE `ba_fence` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fence_data` varchar(500) DEFAULT NULL,
`create_time` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
- 模型
<?php
namespace app\admin\model;
use think\Model;
class Fence extends Model
{
protected $name = 'fence';
protected $autoWriteTimestamp = false;
}
- 前端
<template>
<div class="map-container">
<div id="map" style="height: 400px; width: 50%; margin-left: 200px;"></div>
<div class="input-card" style="margin-left: 200px;margin-top: 15px;">
<h4>添加围栏</h4>
<div>
<el-button @click="addPolygon" :disabled="polygonClosed">添加围栏</el-button>
<el-button @click="clearPolygon">重新绘制</el-button>
<el-button @click="uploadPolygon">上传围栏</el-button>
<el-button @click="nowAddress">定位当前</el-button>
<el-button @click="getFenceData">获取围栏</el-button>
<el-button @click="isInsidePolygon">判断是否在围栏内</el-button>
</div>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
import AMapLoader from '@amap/amap-jsapi-loader'
import {createFence, getFence, isInside} from '/@/api/controllerUrls'
import { ElNotification } from 'element-plus'
export default {
name: 'MapComponent',
setup() {
let marker = null
const map = ref(null)
let polygon = null
let polyline = null
const pathArr = []
const polygonClosed = ref(false)
const initMap = async () => {
AMapLoader.load({
key: 'YOUR_KEY',
version: '2.0'
}).then((AMap) => {
map.value = new AMap.Map('map', {
viewMode: '3D',
zoom: 15,
resizeEnable: true,
mapStyle: 'amap://styles/normal',
center: [121.7789, 31.3312],
showMarker: true
})
marker = new AMap.Marker({
position: [121.7789, 31.3312],
icon: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_bs.png',
draggable: true
})
map.value.add(marker)
map.value.setFitView()
map.value.on('click', (e) => {
const lnglat = e.lnglat
const newPath = [lnglat.lng, lnglat.lat]
if (pathArr.length > 0 && !polygonClosed.value) {
pathArr.push(newPath)
if (polyline) {
polyline.setMap(null)
}
polyline = new AMap.Polyline({
map: map.value,
path: pathArr,
strokeColor: '#3366FF',
strokeOpacity: 1,
strokeWeight: 3,
})
} else {
pathArr.push(newPath)
}
const tolerance = 0.01;
if (pathArr.length > 1 && isCloseEnough(pathArr[pathArr.length - 1], pathArr[0], tolerance, map.value)) {
pathArr.push(pathArr[0]);
addPolygon();
polygonClosed.value = true;
}
})
}).catch((e) => {
console.error(e)
})
}
function isCloseEnough(point1, point2, tolerance, map) {
const [lng1, lat1] = point1;
const [lng2, lat2] = point2;
const distance = Math.sqrt(Math.pow(lng2 - lng1, 2) + Math.pow(lat2 - lat1, 2));
const zoom = map.getZoom();
const adjustedTolerance = tolerance / Math.pow(2, zoom - 10);
return distance <= adjustedTolerance;
}
const addPolygon = () => {
if (polygon) {
polygon.setMap(null)
}
polygon = new AMap.Polygon({
path: pathArr,
fillColor: '#ccebc5',
strokeOpacity: 1,
fillOpacity: 0.5,
strokeColor: '#2b8cbe',
strokeWeight: 1,
strokeStyle: 'dashed',
strokeDasharray: [5, 5],
});
polygon.on('mouseover', () => {
polygon.setOptions({
fillOpacity: 0.7,
fillColor: '#7bccc4',
});
});
polygon.on('mouseout', () => {
polygon.setOptions({
fillOpacity: 0.5,
fillColor: '#ccebc5',
});
});
map.value.add(polygon);
};
const clearPolygon = () => {
if (polygon) {
polygon.setMap(null)
}
if (polyline) {
polyline.setMap(null)
}
pathArr.length = 0;
polygonClosed.value = false;
}
const uploadPolygon = () => {
if (polygonClosed.value) {
console.log('闭合围栏坐标集合:', pathArr);
const formData = new FormData();
formData.append('fence', JSON.stringify(pathArr));
createFence(formData).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});
} else {
console.log('围栏未闭合,请完成多边形并闭合后再上传。');
}
}
const nowAddress = () => {
if (map.value) {
AMap.plugin('AMap.Geolocation', function() {
const geolocation = new AMap.Geolocation({
enableHighAccuracy: true,
timeout: 10000,
buttonOffset: new AMap.Pixel(10, 20),
zoomToAccuracy: true,
buttonPosition: 'RB'
});
map.value.addControl(geolocation);
geolocation.getCurrentPosition();
});
}
};
const getFenceData = () => {
getFence(1).then((res)=> {
const fenceData = JSON.parse(res.data.fence);
drawFenceOnMap(fenceData);
})
}
const drawFenceOnMap = (fenceData) => {
if (map.value && fenceData.length > 0) {
if (polygon) {
polygon.setMap(null);
}
polygon = new AMap.Polygon({
path: fenceData,
fillColor: '#ccebc5',
strokeOpacity: 1,
fillOpacity: 0.5,
strokeColor: '#2b8cbe',
strokeWeight: 1,
strokeStyle: 'dashed',
strokeDasharray: [5, 5],
});
polygon.on('mouseover', () => {
polygon.setOptions({
fillOpacity: 0.7,
fillColor: '#7bccc4',
});
});
polygon.on('mouseout', () => {
polygon.setOptions({
fillOpacity: 0.5,
fillColor: '#ccebc5',
});
});
map.value.add(polygon);
map.value.setFitView(polygon);
}
};
const isInsidePolygon = () => {
if (polygonClosed.value && marker) {
const markerPosition = marker.getPosition();
const formData = new FormData();
formData.append('fence', JSON.stringify(pathArr));
formData.append('point', JSON.stringify(markerPosition));
isInside(formData).then(res => {
if (res.data.isInside == 1) {
ElNotification({message: '坐标在多边形内'});
} else {
ElNotification({message: '坐标在多边形外部'});
}
}).catch(err => {
console.log(err);
});
} else {
console.log('围栏未闭合或marker不存在,请完成多边形并闭合后再判断');
}
}
onMounted(() => {
initMap()
})
return {
addPolygon,
clearPolygon,
uploadPolygon,
polygonClosed,
nowAddress,
getFenceData,
isInsidePolygon
}
}
}
</script>
<style>
.map-container {
margin: 10px 0;
}
#map {
height: 400px;
width: 100%;
}
</style>
- 封装api
web\src\api\controllerUrls.ts
export function getMapData() {
return createAxios({
url: '/admin/dashboard/getMapData',
method: 'get',
})
}
export function createFence(form: any) {
return createAxios({
url: '/admin/dashboard/createFence',
method: 'post',
data: form
})
}
export function getFence(id: any) {
return createAxios({
url: '/admin/dashboard/getFence?id='+id,
method: 'get',
})
}
export function isInside(form: any) {
return createAxios({
url: '/admin/dashboard/isInside',
method: 'post',
data: form
})
}
- 后端接口
<?php
namespace app\admin\controller;
use app\admin\model\Fence;
use app\common\controller\Backend;
use utils\GpsUtils;
class Dashboard extends Backend
{
public function initialize(): void
{
parent::initialize();
}
public function index(): void
{
$this->success('', [
'remark' => get_route_remark()
]);
}
public function createFence()
{
$data = $this->request->post('fence');
$dataArr = json_decode($data, true);
$fence = new Fence();
$fence->fence_data = $data;
if ($fence->save()) {
$this->success('添加围栏成功');
} else {
$this->error('添加围栏失败');
}
}
public function getFence()
{
$id = $this->request->get('id');
if ($id == 1) {
$lastRecord = Fence::order('id', 'desc')->find();
$id = $lastRecord ? $lastRecord->id : 1;
}
$fence = Fence::find($id);
$this->success('', ['fence'=> $fence->fence_data]);
}
public function isInside()
{
$fence = $this->request->post('fence');
$point = $this->request->post('point');
$dataArr = json_decode($fence, true);
$point = str_replace('[', '', $point);
$point = str_replace(']', '', $point);
$res = $this->containsPoint($dataArr, $point);
$this->success('', ['isInside' => (int)$res]);
}
public function containsPoint($vertices, $ponits)
{
$ponitsArr = explode(',', $ponits);
$lat = $ponitsArr[0];
$lng = $ponitsArr[1];
$crossings = 0;
$count = count($vertices);
for ($i = 0; $i < $count; $i++) {
$a = $vertices[$i];
$b = $vertices[($i + 1) % $count];
if ((($a[1] <= $lng) && ($lng < $b[1])) || (($b[1] <= $lng) && ($lng < $a[1]))) {
$vt = ($lng - $a[1]) / ($b[1] - $a[1]);
if ($lat < $a[0] + $vt * ($b[0] - $a[0])) {
$crossings++;
}
}
}
return ($crossings % 2 != 0);
}
}