问题
应用到这个场景的地方还是蛮多的。
数学中的坐标和现实中的经纬度。
- 判断一个坐标点,是否在一个多边形区域中
- 多边形是一个有序的坐标集合构成的。
原理
在计算几何中,多边形中的点( PIP ) 问题询问平面中的给定点是否位于多边形的内部、外部或边界上。它是点定位问题的一个特例,可用于处理几何数据的领域,例如计算机图形学、计算机视觉、地理信息系统(GIS)、运动规划和CAD。
计算机图形学中对该问题的早期描述显示了早在 1974 年就使用的两种常见方法(光线投射和角度求和)。
本文只介绍 光线投射 方法:
确定点是在简单多边形内部还是外部的一种简单方法是测试从该点开始并沿任何固定方向行进的射线与多边形边缘相交的次数。如果该点位于多边形的外部,则光线将与其边缘相交偶数次。如果点在多边形的内部,那么它将与边缘相交奇数次。多边形边缘上的点的状态取决于光线相交算法的细节。
须知
- 纬度(φ)是地球表面一个点的南北地理位置的表示法。纬度与经度通常一起使用以确定地表上某点的精确位置。
纬度是一个角度,其范围从赤道的0度到南北极的90度。在英文文本中,纬度通常使用小写希腊字母phi (φ)来表示。它以度、分、秒或者小数形式的度来计量,再附上N或S来表示北纬或南纬。纬度相同的连线形成与赤道平行的大圆。赤道、南回归线、北回归线、南极圈和北极圈是特殊的纬线。
粗略来说:纬度就是数学中的纵坐标(Y),表现为从南到北的一个个套在地球上的水平的圈。
- 经度是一种用于确定地球表面上不同点东西位置的地理坐标。
经度是一种角度量,通常用度来表示,并被记作希腊字母λ(lambda)。子午线穿过南极和北极并把相同经度的点连起来。按照惯例,本初子午线是经过伦敦格林威治皇家天文台的子午线,是0度经线所在地。其他位置的经度是通过测量其从本初子午线向东或向西经过的角度得到的,经度的范围为从本初子午线0° 向东至180°向西至180° W。具体来说,某位置的经度是一个通过本初子午线的平面和一个通过南极、北极和该位置的平面所组成的二面角。(这就组成了一个右手坐标系,其z轴(右手拇指)从地球中心指向北极方向,其x轴(右手食指)从地球中心指向本初子午线与赤道的交点。)
- 纬度相当于走了地球的半圈,所以是 -90 - 90
- 经度相当于走了地球的一圈,所以是-180-180
思考
代码需要模拟一个点向右的射线和多边形边的交点的个数:
- 这个点不管向哪个方向发出射线都可以,但是我们取一个计算比较简单的方向。
- 代码怎么构建多边形
想到 计算线与多边形的交点并不需要构建多边形。
只需要将多边形的各个边拿出来遍历,分别看每个边与射线的交点个数。
两个边又如何构建呢?
也不需要构建,只需要计算点的位置即可。因为我们的射线是水平方向无限长的。
- 如果射线起点的 纬度(Y) 比 边的两个顶点 的 (Y) 都大或者都小,那就肯定不可能相交啦。
- 如果纬度相交了,就要看经度了。这儿就开始复杂了,因为他不是位于两点经度之间就表示相交了。
-
如果边是垂直的,那就直接比较经度(X)
-
如果边是斜线的,
- 如果射线的起点的经度在边的两点的左侧,那就是相交了
- 如果在右侧,那就不可能相交了
- 如果在两点中间- 如果斜线是降的(左上到右下),连接射线点与右下单,计算出角度比较 ,小于原有边与X 的夹角,就是相交了。 - 如果斜线是升的(左下到右上),同样的道理。
-
分析完之后代码怎么写已经很清楚了。
但是以上处理还可以进一步优化,在进行边相交计算之前,给多边形用一个更大的矩形包住。如果点不在矩形内,那就没有比较进行后续的比较了。
代码
- 坐标点 modal
package com.rrz.o2o.region.model;
import com.vito.framework.base.model.Entity;
import java.util.Objects;
public class MapPoint extends Entity {
private Double longitude;
private Double latitude;
public MapPoint(Double longitude, Double latitude) {
this.longitude = longitude;
this.latitude = latitude;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
MapPoint point = (MapPoint) o;
return Objects.equals(longitude, point.longitude) &&
Objects.equals(latitude, point.latitude);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), longitude, latitude);
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
}
- 工具类
package com.vito