背景
在地图功能开发过程中,遇到电子围栏问题。判断一个坐标是否在一个多边形的电子围栏中。
原理
以坐标点向左发射射线,判断射线与围栏的每一条边是否存在交点。将交点统计,为偶数则不在围栏之内,反之则在围栏之内。
代码实现
坐标实体类
import lombok.Data;
import java.math.BigDecimal;
/**
* 坐标
*
* @author zhouyaoming
* @version 1.0.0
*/
@Data
public class CoordinateForm {
/**
* 经度
*/
private BigDecimal longitude;
/**
* 纬度
*/
private BigDecimal latitude;
}
** 模糊计算**
/**
* 计算坐标是否在电子围栏之内(模糊计算)
* @param coordinateForm
* @param ps
* @return
*/
public static boolean isPtInPolys( List<CoordinateForm> ps,CoordinateForm coordinateForm) {
double ALon = coordinateForm.getLongitude().doubleValue();
double ALat = coordinateForm.getLatitude().doubleValue();
int iSum, iCount, iIndex;
double dLonFirst = 0, dLonSecond = 0, dLatFirst = 0, dLatSecond = 0, dLon;
if (ps.size() < Constant.THREE) {
return false;
}
iSum = 0;
iCount = ps.size();
for (iIndex = 0; iIndex < iCount; iIndex++) {
dLonFirst = ps.get(iIndex).getLongitude().doubleValue();
dLatFirst = ps.get(iIndex).getLatitude().doubleValue();
if (iIndex == iCount - 1) {
dLonSecond = ps.get(0).getLongitude().doubleValue();
dLatSecond = ps.get(0).getLatitude().doubleValue();
} else {
dLonSecond = ps.get(iIndex + 1).getLongitude().doubleValue();
dLatSecond = ps.get(iIndex + 1).getLatitude().doubleValue();
}
// 以下语句判断A点是否在边的两端点的水平平行线之间,在则可能有交点,开始判断交点是否在左射线上
if (((ALat >= dLatFirst) && (ALat < dLatSecond)) || ((ALat >= dLatSecond) && (ALat < dLatFirst))) {
if (Math.abs(dLatFirst - dLatSecond) > 0) {
//得到 A点向左射线与边的交点的x坐标:
dLon = dLonFirst - ((dLonFirst - dLonSecond) * (dLatFirst - ALat)) / (dLatFirst - dLatSecond);
// 如果交点在A点左侧(说明是做射线与 边的交点),则射线与边的全部交点数加一:
if (dLon < ALon) {
iSum++;
}
}
}
}
if ((iSum % Constant.TWO) != 0) {
return true;
}
return false;
}
精确计算
/**
* 计算坐标是否在电子围栏之内(精确计算)
*
* @param ps 电子围栏坐标
* @param coordinateForm 所求坐标位置
* @return
*/
public static boolean isPtInPoly(List<CoordinateForm> ps, CoordinateForm coordinateForm) {
BigDecimal ALon = coordinateForm.getLongitude();
BigDecimal ALat = coordinateForm.getLatitude();
int iSum, iCount, iIndex;
BigDecimal dLonFirst = new BigDecimal(0), dLonSecond = new BigDecimal(0), dLatFirst = new BigDecimal(0), dLatSecond = new BigDecimal(0), dLon;
if (ps.size() < Constant.THREE) {
return false;
}
iSum = 0;
iCount = ps.size();
for (iIndex = 0; iIndex < iCount; iIndex++) {
dLonFirst = ps.get(iIndex).getLongitude();
dLatFirst = ps.get(iIndex).getLatitude();
// 当取最后一个坐标时,将围栏的第一个坐标赋值给坐标second
if (iIndex == iCount - 1) {
dLonSecond = ps.get(0).getLongitude();
dLatSecond = ps.get(0).getLatitude();
} else {
dLonSecond = ps.get(iIndex + 1).getLongitude();
dLatSecond = ps.get(iIndex + 1).getLatitude();
}
// 以下语句判断A点是否在边的两端点的水平平行线之间,在则可能有交点,开始判断交点是否在左射线上
if (((ALat.compareTo(dLatFirst) >= 0) && (ALat.compareTo(dLatSecond) < 0)) || ((ALat.compareTo(dLatSecond) >= 0) && (ALat.compareTo(dLatFirst) < 0))) {
if (dLatFirst.subtract(dLatSecond).abs().compareTo(new BigDecimal(0)) > 0) {
//得到 A点向左射线与边的交点的x坐标:
dLon = dLonFirst.subtract((dLonFirst.subtract(dLonSecond)).multiply(dLatFirst.subtract(ALat))).divide(dLatFirst.subtract(dLatSecond), 6, BigDecimal.ROUND_HALF_EVEN);
// 如果交点在A点左侧(说明是做射线与 边的交点),则射线与边的全部交点数加一:
if (dLon.compareTo(ALon) < 0) {
iSum++;
}
}
}
}
if ((iSum % Constant.TWO) != 0) {
return true;
}
return false;
}