Files
smart-crop-ui/src/lib/geoFenceTestData.ts

274 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { GeoFence, GeoFenceAlert } from '../types/equipment';
/**
* 生成测试用的电子围栏数据
*/
export function generateTestGeoFences(machineryIds: string[]): GeoFence[] {
const now = new Date().toISOString();
return [
// 矩形作业区
{
id: 'fence-test-1',
name: '1号作业区',
type: 'polygon',
points: [
{ latitude: 36.6512, longitude: 117.1201 },
{ latitude: 36.6532, longitude: 117.1201 },
{ latitude: 36.6532, longitude: 117.1251 },
{ latitude: 36.6512, longitude: 117.1251 },
],
machineryIds: machineryIds.slice(0, 2),
alertOnExit: true,
alertOnEnter: false,
countWorkHours: true,
enabled: true,
createdAt: now,
updatedAt: now,
createdBy: '系统管理员',
},
// 圆形禁入区
{
id: 'fence-test-2',
name: '东侧水塘禁入区',
type: 'circle',
center: { latitude: 36.6500, longitude: 117.1200 },
radius: 200,
machineryIds: machineryIds,
alertOnExit: false,
alertOnEnter: true,
countWorkHours: false,
enabled: true,
createdAt: now,
updatedAt: now,
createdBy: '系统管理员',
},
// 大型多边形作业区
{
id: 'fence-test-3',
name: '2号地块-西区',
type: 'polygon',
points: [
{ latitude: 36.6480, longitude: 117.1150 },
{ latitude: 36.6500, longitude: 117.1150 },
{ latitude: 36.6510, longitude: 117.1180 },
{ latitude: 36.6500, longitude: 117.1200 },
{ latitude: 36.6480, longitude: 117.1190 },
],
machineryIds: machineryIds.slice(0, 1),
alertOnExit: true,
alertOnEnter: false,
countWorkHours: true,
enabled: true,
createdAt: now,
updatedAt: now,
createdBy: '系统管理员',
},
// 休息区(圆形)
{
id: 'fence-test-4',
name: '农机停放区',
type: 'circle',
center: { latitude: 36.6550, longitude: 117.1100 },
radius: 100,
machineryIds: machineryIds,
alertOnExit: false,
alertOnEnter: false,
countWorkHours: false,
enabled: true,
createdAt: now,
updatedAt: now,
createdBy: '系统管理员',
},
];
}
/**
* 生成测试用的围栏报警数据
*/
export function generateTestGeoFenceAlerts(
fences: GeoFence[],
machineryData: { id: string; name: string }[]
): GeoFenceAlert[] {
const alerts: GeoFenceAlert[] = [];
// 生成一些历史报警
if (fences.length > 0 && machineryData.length > 0) {
const fence1 = fences[0];
const machinery1 = machineryData[0];
alerts.push({
id: 'alert-test-1',
fenceId: fence1.id,
fenceName: fence1.name,
machineryId: machinery1.id,
machineryName: machinery1.name,
alertType: 'exit',
location: { latitude: 36.6510, longitude: 117.1252 },
alertedAt: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), // 2小时前
acknowledged: false,
});
}
if (fences.length > 1 && machineryData.length > 1) {
const fence2 = fences[1];
const machinery2 = machineryData[1];
alerts.push({
id: 'alert-test-2',
fenceId: fence2.id,
fenceName: fence2.name,
machineryId: machinery2.id,
machineryName: machinery2.name,
alertType: 'enter',
location: { latitude: 36.6501, longitude: 117.1201 },
alertedAt: new Date(Date.now() - 30 * 60 * 1000).toISOString(), // 30分钟前
acknowledged: true,
acknowledgedBy: '值班员张三',
acknowledgedAt: new Date(Date.now() - 25 * 60 * 1000).toISOString(),
});
}
return alerts;
}
/**
* 验证围栏坐标是否有效
*/
export function validateGeoFence(fence: Partial<GeoFence>): { valid: boolean; errors: string[] } {
const errors: string[] = [];
if (!fence.name || fence.name.trim() === '') {
errors.push('围栏名称不能为空');
}
if (!fence.type) {
errors.push('必须选择围栏类型');
}
if (fence.type === 'circle') {
if (!fence.center) {
errors.push('圆形围栏必须设置中心点');
} else {
if (!isValidLatitude(fence.center.latitude)) {
errors.push('中心点纬度无效(应在-90到90之间');
}
if (!isValidLongitude(fence.center.longitude)) {
errors.push('中心点经度无效(应在-180到180之间');
}
}
if (!fence.radius || fence.radius <= 0) {
errors.push('圆形围栏半径必须大于0');
}
}
if (fence.type === 'polygon') {
if (!fence.points || fence.points.length < 3) {
errors.push('多边形围栏至少需要3个顶点');
} else {
fence.points.forEach((point, index) => {
if (!isValidLatitude(point.latitude)) {
errors.push(`顶点${index + 1}的纬度无效`);
}
if (!isValidLongitude(point.longitude)) {
errors.push(`顶点${index + 1}的经度无效`);
}
});
}
}
if (!fence.machineryIds || fence.machineryIds.length === 0) {
errors.push('必须至少关联一台农机');
}
if (!fence.alertOnEnter && !fence.alertOnExit) {
errors.push('建议至少启用一种报警方式(进入或离开)');
}
return {
valid: errors.length === 0,
errors,
};
}
/**
* 验证纬度是否有效
*/
function isValidLatitude(lat: number): boolean {
return !isNaN(lat) && lat >= -90 && lat <= 90;
}
/**
* 验证经度是否有效
*/
function isValidLongitude(lng: number): boolean {
return !isNaN(lng) && lng >= -180 && lng <= 180;
}
/**
* 计算两点之间的距离(米)
* 使用 Haversine 公式
*/
export function calculateDistance(
point1: { latitude: number; longitude: number },
point2: { latitude: number; longitude: number }
): number {
const R = 6371000; // 地球半径,米
const lat1 = (point1.latitude * Math.PI) / 180;
const lat2 = (point2.latitude * Math.PI) / 180;
const deltaLat = ((point2.latitude - point1.latitude) * Math.PI) / 180;
const deltaLng = ((point2.longitude - point1.longitude) * Math.PI) / 180;
const a =
Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
/**
* 判断点是否在圆形围栏内
*/
export function isPointInCircle(
point: { latitude: number; longitude: number },
center: { latitude: number; longitude: number },
radius: number
): boolean {
const distance = calculateDistance(point, center);
return distance <= radius;
}
/**
* 判断点是否在多边形围栏内
* 使用射线法Ray Casting Algorithm
*/
export function isPointInPolygon(
point: { latitude: number; longitude: number },
polygon: { latitude: number; longitude: number }[]
): boolean {
let inside = false;
const x = point.longitude;
const y = point.latitude;
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const xi = polygon[i].longitude;
const yi = polygon[i].latitude;
const xj = polygon[j].longitude;
const yj = polygon[j].latitude;
const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
if (intersect) {
inside = !inside;
}
}
return inside;
}