判断点与多边形的位置关系

[b]遇到一个需求,给定一个点的坐标以及一个多边形的所有顶点坐标。要求能够判断这个点是在多边形内,还是在多边形外?[/b]

[b]【参考文献】:[/b]
1、两条直线的关系
[url]http://www.cnblogs.com/devymex/archive/2010/08/19/1803885.html[/url]
2、点与多边形的关系
[url]http://wenku.baidu.com/view/5e3913a2b0717fd5360cdccf.html?qq-pf-to=pcqq.c2c[/url]

经过在网上的一番搜索,发现目前比较通用的就是射线法,而我采用的就是X轴射线法。主要理论来源于西安交大的一篇论文(即参考文献的第二条)

[b]代码讲解:[/b]
主要的类有两个:一个是坐标点的抽象类,另一个是位置关系判断工具类。

[b]1、是坐标点的抽象类[/b]

package com.niux.crm.core.common.bmap;

/**
* 用于构造百度地图中的经纬度点
*
* @author zhengtian
* @date 2013-8-5 下午02:54:41
*/
public class BmapPoint {
private double lng;// 经度
private double lat;// 纬度

public BmapPoint() {

}

public BmapPoint(double lng, double lat) {
this.lng = lng;
this.lat = lat;
}

@Override
public boolean equals(Object obj) {
if (obj instanceof BmapPoint) {
BmapPoint bmapPoint = (BmapPoint) obj;
return (bmapPoint.getLng() == lng && bmapPoint.getLat() == lat) ? true : false;
} else {
return false;
}
}

public double getLng() {
return lng;
}

public void setLng(double lng) {
this.lng = lng;
}

public double getLat() {
return lat;
}

public void setLat(double lat) {
this.lat = lat;
}
}



[b]2、位置关系判断工具类[/b]

点与多边形的位置关系的判定规则:1、,根据多边形的坐标,虚拟出一个外包矩形,主要是为了提前过滤不相关的点,减少运算量。2、然后判断是否有重合的点。3、判断点与斜线的交点。4、判断点过顶点的情况。5、判断点与边重合的情况。6、判断点在边上的情况。

其中点过顶点,以及点与边重合的情况,主要采用了加权边的思想,论文与代码中有注释。


package com.niux.crm.core.common.bmap;

import java.util.Arrays;


/**
* 用于点与多边形位置关系的判断
*
* @author zhengtian
* @date 2013-8-5 上午11:59:35
*/
public class GraphUtils {

/**
* 判断点是否在多边形内(基本思路是用交点法)
*
* @param point
* @param boundaryPoints
* @return
*/
public static boolean isPointInPolygon(BmapPoint point, BmapPoint[] boundaryPoints) {
// 防止第一个点与最后一个点相同
if (boundaryPoints != null && boundaryPoints.length > 0
&& boundaryPoints[boundaryPoints.length - 1].equals(boundaryPoints[0])) {
boundaryPoints = Arrays.copyOf(boundaryPoints, boundaryPoints.length - 1);
}
int pointCount = boundaryPoints.length;

// 首先判断点是否在多边形的外包矩形内,如果在,则进一步判断,否则返回false
if (!isPointInRectangle(point, boundaryPoints)) {
return false;
}

// 如果点与多边形的其中一个顶点重合,那么直接返回true
for (int i = 0; i < pointCount; i++) {
if (point.equals(boundaryPoints[i])) {
return true;
}
}

/**
* 基本思想是利用X轴射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则在多边形内。还会考虑一些特殊情况,如点在多边形顶点上
* , 点在多边形边上等特殊情况。
*/
// X轴射线与多边形的交点数
int intersectPointCount = 0;
// X轴射线与多边形的交点权值
float intersectPointWeights = 0;
// 浮点类型计算时候与0比较时候的容差
double precision = 2e-10;
// 边P1P2的两个端点
BmapPoint point1 = boundaryPoints[0], point2;
// 循环判断所有的边
for (int i = 1; i <= pointCount; i++) {
point2 = boundaryPoints[i % pointCount];

/**
* 如果点的y坐标在边P1P2的y坐标开区间范围之外,那么不相交。
*/
if (point.getLat() < Math.min(point1.getLat(), point2.getLat())
|| point.getLat() > Math.max(point1.getLat(), point2.getLat())) {
point1 = point2;
continue;
}

/**
* 此处判断射线与边相交
*/
if (point.getLat() > Math.min(point1.getLat(), point2.getLat())
&& point.getLat() < Math.max(point1.getLat(), point2.getLat())) {// 如果点的y坐标在边P1P2的y坐标开区间内
if (point1.getLng() == point2.getLng()) {// 若边P1P2是垂直的
if (point.getLng() == point1.getLng()) {
// 若点在垂直的边P1P2上,则点在多边形内
return true;
} else if (point.getLng() < point1.getLng()) {
// 若点在在垂直的边P1P2左边,则点与该边必然有交点
++intersectPointCount;
}
} else {// 若边P1P2是斜线
if (point.getLng() <= Math.min(point1.getLng(), point2.getLng())) {// 点point的x坐标在点P1和P2的左侧
++intersectPointCount;
} else if (point.getLng() > Math.min(point1.getLng(), point2.getLng())
&& point.getLng() < Math.max(point1.getLng(), point2.getLng())) {// 点point的x坐标在点P1和P2的x坐标中间
double slopeDiff = 0.0d;
if (point1.getLat() > point2.getLat()) {
slopeDiff = (point.getLat() - point2.getLat()) / (point.getLng() - point2.getLng())
- (point1.getLat() - point2.getLat()) / (point1.getLng() - point2.getLng());
} else {
slopeDiff = (point.getLat() - point1.getLat()) / (point.getLng() - point1.getLng())
- (point2.getLat() - point1.getLat()) / (point2.getLng() - point1.getLng());
}
if (slopeDiff > 0) {
if (slopeDiff < precision) {// 由于double精度在计算时会有损失,故匹配一定的容差。经试验,坐标经度可以达到0.0001
// 点在斜线P1P2上
return true;
} else {
// 点与斜线P1P2有交点
intersectPointCount++;
}
}
}
}
} else {
// 边P1P2水平
if (point1.getLat() == point2.getLat()) {
if (point.getLng() <= Math.max(point1.getLng(), point2.getLng())
&& point.getLng() >= Math.min(point1.getLng(), point2.getLng())) {
// 若点在水平的边P1P2上,则点在多边形内
return true;
}
}
/**
* 判断点通过多边形顶点
*/
if (((point.getLat() == point1.getLat() && point.getLng() < point1.getLng()))
|| (point.getLat() == point2.getLat() && point.getLng() < point2.getLng())) {
if (point2.getLat() < point1.getLat()) {
intersectPointWeights += -0.5;
} else if (point2.getLat() > point1.getLat()) {
intersectPointWeights += 0.5;
}
}
}
point1 = point2;
}

if ((intersectPointCount + Math.abs(intersectPointWeights)) % 2 == 0) {// 偶数在多边形外
return false;
} else { // 奇数在多边形内
return true;
}
}

/**
* 判断点是否在矩形内在矩形边界上,也算在矩形内(根据这些点,构造一个外包矩形)
*
* @param point
* 点对象
* @param boundaryPoints
* 矩形边界点
* @return
*/
public static boolean isPointInRectangle(BmapPoint point, BmapPoint[] boundaryPoints) {
BmapPoint southWestPoint = getSouthWestPoint(boundaryPoints); // 西南角点
BmapPoint northEastPoint = getNorthEastPoint(boundaryPoints); // 东北角点
return (point.getLng() >= southWestPoint.getLng() && point.getLng() <= northEastPoint.getLng()
&& point.getLat() >= southWestPoint.getLat() && point.getLat() <= northEastPoint.getLat());

}

/**
* 根据这组坐标,画一个矩形,然后得到这个矩形西南角的顶点坐标
*
* @param vertexs
* @return
*/
private static BmapPoint getSouthWestPoint(BmapPoint[] vertexs) {
double minLng = vertexs[0].getLng(), minLat = vertexs[0].getLat();
for (BmapPoint bmapPoint : vertexs) {
double lng = bmapPoint.getLng();
double lat = bmapPoint.getLat();
if (lng < minLng) {
minLng = lng;
}
if (lat < minLat) {
minLat = lat;
}
}
return new BmapPoint(minLng, minLat);
}

/**
* 根据这组坐标,画一个矩形,然后得到这个矩形东北角的顶点坐标
*
* @param vertexs
* @return
*/
private static BmapPoint getNorthEastPoint(BmapPoint[] vertexs) {
double maxLng = 0.0d, maxLat = 0.0d;
for (BmapPoint bmapPoint : vertexs) {
double lng = bmapPoint.getLng();
double lat = bmapPoint.getLat();
if (lng > maxLng) {
maxLng = lng;
}
if (lat > maxLat) {
maxLat = lat;
}
}
return new BmapPoint(maxLng, maxLat);
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
射线法是一种常用的判断多边形关系的算法。下面是使用 SQL Server 实现射线法判断多边形关系的一种可能方法: 首先,假设我们有一个名为 Points 的表,其中包含多边形的顶坐标,以及一个名为 TestPoint 的表,其中包含要测试的的坐标。 我们可以使用以下 SQL 查询来判断多边形关系: ```sql -- 创建示例表 CREATE TABLE Points ( ID INT, X FLOAT, Y FLOAT ); CREATE TABLE TestPoint ( X FLOAT, Y FLOAT ); -- 插入多边形数据 INSERT INTO Points (ID, X, Y) VALUES (1, 0, 0), (2, 0, 4), (3, 4, 4), (4, 4, 0); -- 插入要测试的数据 INSERT INTO TestPoint (X, Y) VALUES (2, 2); -- 计算射线与多边形的交数量 WITH CTE AS ( SELECT p1.X AS x1, p1.Y AS y1, p2.X AS x2, p2.Y AS y2, tp.X AS tx, tp.Y AS ty FROM Points p1 JOIN Points p2 ON p1.ID = p2.ID + 1 OR (p1.ID = (SELECT MAX(ID) FROM Points) AND p2.ID = 1) CROSS JOIN TestPoint tp ) SELECT COUNT(*) % 2 AS IsInside FROM CTE WHERE CASE WHEN y1 = y2 THEN ty = y1 AND tx > x1 AND tx < x2 ELSE (ty - y1) / (y2 - y1) * (x2 - x1) > (tx - x1) END; ``` 上述查询首先创建了名为 Points 的表,并插入了多边形的顶数据。然后创建了名为 TestPoint 的表,并插入要测试的的数据。 接下来,使用 CTE(通用表达式)来计算射线与多边形的交数量。最后,查询返回一个布尔值 IsInside,表示测试是否在多边形内部。如果 IsInside 的值为 1,则表示多边形内部;如果值为 0,则表示多边形外部。 请注意,这只是一种实现方式,具体的实现可能因数据库结构和需求而有所不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值