需求背景:业务上需要给客户提供一个在地图上自已画区域作电子围栏用,区域一般为圆形、矩形和多边形,我们这只探讨多边形区域的实现和验证方法。
实现方式:
前、后端分离项目,即vue+api模式、后端有一个增加区域的接口,为了便于理解,我们假设该接口仅为增加多边形区域的接口(实际为三合一接口),接收区域名称和一组坐标集合(多边形顶点坐标)两个参数。
参考高德地图-猎鹰轨迹服务-创建多边形围栏接口(v1/track/geofence/add/polygon)
https://lbs.amap.com/api/track/lieying-kaifa/api/track_fence 。
前端通过使用地图上绘制区域组件,绘制一个多边形区域,绘制完成后会生成一组坐标集合,前端将该坐标集通过调用API接口提交到后端。
一般情况下,在地图上绘制的多边形是闭合,生成的坐标点也是按顺时针(或反时针)排列的最外侧边缘的拐角顶点。
需求:
后端要对传入的坐标集进行效验,确保传入的这个数组内的坐标是有效的多边形区域。
有效是指:
判断给定的坐标集合是否能形成一个有效的多边形,可以使用射线交点法。以下是一个简单的C#实现:
csharp
Copy code
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<Point> polygon = new List<Point>
{
new Point(0, 0),
new Point(0, 5),
new Point(5, 5),
new Point(5, 0),
new Point(2, 2)
};
Console.WriteLine(IsPolygonValid(polygon)); // Output: True
}
static bool IsPolygonValid(List<Point> polygon)
{
int n = polygon.Count;
// Check if there are at least 3 vertices
if (n < 3)
return false;
// Initialize winding number
int windingNumber = 0;
// Loop through each edge of the polygon
for (int i = 0; i < n; i++)
{
Point currentVertex = polygon[i];
Point nextVertex = polygon[(i + 1) % n];
// Check for upward crossing
if (currentVertex.Y <= nextVertex.Y)
{
if (currentVertex.Y < 0 && nextVertex.Y >= 0)
{
if (IsLeft(currentVertex, nextVertex, new Point(0, 0)) > 0)
windingNumber++;
}
}
// Check for downward crossing
else
{
if (currentVertex.Y >= 0 && nextVertex.Y < 0)
{
if (IsLeft(currentVertex, nextVertex, new Point(0, 0)) < 0)
windingNumber--;
}
}
}
// The polygon is valid if the winding number is not zero
return windingNumber != 0;
}
static double IsLeft(Point P0, Point P1, Point P2)
{
return ((P1.X - P0.X) * (P2.Y - P0.Y) - (P2.X - P0.X) * (P1.Y - P0.Y));
}
}
class Point
{
public double X { get; set; }
public double Y { get; set; }
public Point(double x, double y)
{
X = x;
Y = y;
}
}
这个实现使用射线交点法,通过射线从给定点(例如原点)向右水平射出,统计与多边形边界的交点数量来判断是否在多边形内部。这是一个简化的实现,对于一些边界情况可能需要进一步处理。请根据具体需求进行适当调整和测试。
这是AI生成的吗?
点大于三个,且首尾点相同,那么肯定能形成多边形,就是多边形是否是你想要的不好判定
是的,这种情况只需要排除所有点不在同一直线上,和去掉重复的即可。
如果要检查只有一个面积区域,判断所有相邻的点之间的线段是否相交应该可以。
百度文心一言
问:
回:
using System;
using System.Collections.Generic;
using System.Linq;
public class PolygonChecker
{
public static bool IsCoordinateInPolygon(Coordinate targetCoordinate, List<Coordinate> polygonCoordinates)
{
// 使用射线交叉法判断点是否在多边形内
return CrossingCount(targetCoordinate, polygonCoordinates) % 2 == 1;
}
private static int CrossingCount(Coordinate point, List<Coordinate> polygon)
{
int count = 0;
for (int i = 0; i < polygon.Count; i++)
{
int j = (i + 1) % polygon.Count; // 获取下一个索引
if (CrossesLine(point, polygon[i], polygon[j]))
{
count++;
}
}
return count;
}
private static bool CrossesLine(Coordinate point, Coordinate lineStart, Coordinate lineEnd)
{
double px = point.Longitude - lineStart.Longitude;
double py = point.Latitude - lineStart.Latitude;
double qx = lineEnd.Longitude - lineStart.Longitude;
double qy = lineEnd.Latitude - lineStart.Latitude;
double r = qx * py - qy * px;
if (r == 0) return false; // 线段平行或重合,点在直线上则返回false
return (px * qy - py * qx) * r > 0; // 根据射线交叉法判断点是否在线段内部或与线段相交
}
}
List<Coordinate> coordinates = new List<Coordinate>
{
new Coordinate { Latitude = 40.7128, Longitude = -74.0060 }, // 第一个点(例如:纽约的经纬度)
new Coordinate { Latitude = 37.7749, Longitude = -122.4194 }, // 第二个点(例如:旧金山的经纬度)
// ... 添加更多点以形成多边形 ...
};
Coordinate targetPoint = new Coordinate { Latitude = 37.5662, Longitude = -122.0924 }; // 要检查的点(可以是某个城市的经纬度)
bool isInsidePolygon = PolygonChecker.IsCoordinateInPolygon(targetPoint, coordinates); // 如果返回true,则点在多边形内;否则,不在多边形内。
科大讯飞星火AI
问:
请用c#实现一个判断指定的一组坐标集合(list类型,长度大于3)是否能形成一个闭合区域
回:
using System;
using System.Collections.Generic;
using System.Linq;
public class Coordinate
{
public double X { get; set; }
public double Y { get; set; }
}
public class Program
{
public static void Main()
{
List<Coordinate> coordinates = new List<Coordinate>
{
new Coordinate { X = 0, Y = 0 },
new Coordinate { X = 1, Y = 0 },
new Coordinate { X = 1, Y = 1 },
new Coordinate { X = 0, Y = 1 }
};
bool isClosedArea = IsClosedArea(coordinates);
Console.WriteLine(isClosedArea);
}
public static bool IsClosedArea(List<Coordinate> coordinates)
{
if (coordinates == null || coordinates.Count <= 3)
{
return false;
}
double area = 0;
for (int i = 0; i < coordinates.Count - 1; i++)
{
area += coordinates[i].X * coordinates[i + 1].Y - coordinates[i + 1].X * coordinates[i].Y;
}
area += coordinates[coordinates.Count - 1].X * coordinates[0].Y - coordinates[0].X * coordinates[coordinates.Count - 1].Y;
return Math.Abs(area) > 1e-6;
}
}
文心一言第二次
问:
请用c#实现一个判断给定的多个坐标点(数量大于3)是否能形成一个闭合区域(三角形、四边形或多边形)
回:
要判断一组坐标点是否能形成一个闭合的多边形区域,可以使用欧几里得距离算法来判断两点之间的距离。下面是一个简单的C#示例代码,可以用来判断一组坐标点是否可以形成一个闭合的多边形区域:
using System;
using System.Collections.Generic;
using System.Linq;
public class PolygonChecker
{
public static bool IsPolygonValid(List<Coordinate> coordinates)
{
if (coordinates.Count < 3)
{
throw new ArgumentException("At least 3 coordinates are required to form a polygon.");
}
// 检查是否存在重复的坐标点
var uniqueCoordinates = coordinates.Distinct().ToList();
if (uniqueCoordinates.Count != coordinates.Count)
{
throw new ArgumentException("Duplicate coordinates are not allowed.");
}
// 检查是否所有边都闭合
for (int i = 0; i < coordinates.Count - 1; i++)
{
Coordinate start = coordinates[i];
Coordinate end = coordinates[i + 1];
if (!IsLineSegmentValid(start, end))
{
return false;
}
}
return true; // 所有边都闭合,形成有效的多边形区域
}
private static bool IsLineSegmentValid(Coordinate start, Coordinate end)
{
// 计算两点之间的距离的平方差,如果小于一个很小的阈值(例如0.00001),则认为两点是同一点或几乎重合的线段。
double distanceSquared = (start.Longitude - end.Longitude) * (start.Longitude - end.Longitude) +
(start.Latitude - end.Latitude) * (start.Latitude - end.Latitude);
double threshold = 0.00001; // 可以根据实际情况调整这个阈值
return distanceSquared < threshold;
}
}
这些生成的代码,简单的传入一个正方形都不能通过。
测试数据
一、 4个坐标点(正方形),从左下角反时针排序。
100、100
102、100
102、102
100、102
二、4个坐标点(正方形),从左上角顺时针排序。
100、102
102、102
102、100
100、100