判断点是否在任意多边形内

最近项目用到:在Google map上判断事发地点,是否在管辖区域内。也就是典型的判断一个点是否在不规则任意多边形内的例子。

但是Google Map没有提供相应的api,找资料发现百度地图提供了一个工具类,肿么办,为了一个工具类,加入百度地图吗,操蛋,这是不可能的!

百度地图api链接:http://wiki.lbsyun.baidu.com/cms/androidsdk/doc/v3_7_0/com/baidu/mapapi/utils/SpatialRelationUtil.html

Point Inclusion

• 给定一个点和一个不规则多边形,如果判断点在多边形内部还是外部?

• 方向有助于在线性时间解决这个问题!

Point Inclusion — Part II

• 在每个点的右侧绘制一条水平线,并延伸到无穷远。(水平射线)

• 计算水平射线与多边形相交的次数。

我们有结论:

- 偶数 ⇒ 点在外部

- 奇数 ⇒ 点在内部

• d 和 g点怎么办?   Degeneracy! (在判断一个点的水平射线和多边形一个边是否相交时,依据是:点的竖向坐标y 是否在 线段的竖向坐标(Ymin,Ymax]范围内,而d g点是y值完全等于多边形某一点的y值。而多边形的一个点必然关联两条线段。所以在判断空间关系时,无论取开区间(Ymin,Ymax) 还是闭区间[Ymin,Ymax], 必然造成双重计数,不影响结论)

具体演示效果见

GitHub:https://github.com/shaoshuai904/GoogleMap_Demo

下面是代码部分:

/**
 * Polygon 与 Point 空间关系 工具类
 *
 * @author maple
 */
public class SpatialRelationUtil {

    /**
     * 返回一个点是否在一个多边形区域内(推荐)
     *
     * @param mPoints 多边形坐标点列表
     * @param point   待判断点
     * @return true 多边形包含这个点,false 多边形未包含这个点。
     */
    public static boolean isPolygonContainsPoint1(List<LatLng> mPoints, LatLng point) {
        LatLngBounds.Builder boundsBuilder = LatLngBounds.builder();
        for (LatLng ll : mPoints)
            boundsBuilder.include(ll);
        // 如果point不在多边形Bounds范围内,直接返回false。
        if (boundsBuilder.build().contains(point)) {
            return isPolygonContainsPoint(mPoints, point);
        } else {
            return false;
        }
    }

    /**
     * 返回一个点是否在一个多边形区域内
     *
     * @param mPoints 多边形坐标点列表
     * @param point   待判断点
     * @return true 多边形包含这个点,false 多边形未包含这个点。
     */
    public static boolean isPolygonContainsPoint(List<LatLng> mPoints, LatLng point) {
        int nCross = 0;
        for (int i = 0; i < mPoints.size(); i++) {
            LatLng p1 = mPoints.get(i);
            LatLng p2 = mPoints.get((i + 1) % mPoints.size());
            // 取多边形任意一个边,做点point的水平延长线,求解与当前边的交点个数
            // p1p2是水平线段,要么没有交点,要么有无限个交点
            if (p1.longitude == p2.longitude)
                continue;
            // point 在p1p2 底部 --> 无交点
            if (point.longitude < Math.min(p1.longitude, p2.longitude))
                continue;
            // point 在p1p2 顶部 --> 无交点
            if (point.longitude >= Math.max(p1.longitude, p2.longitude))
                continue;
            // 求解 point点水平线与当前p1p2边的交点的 X 坐标
            double x = (point.longitude - p1.longitude) * (p2.latitude - p1.latitude) / (p2.longitude - p1.longitude) + p1.latitude;
            if (x > point.latitude) // 当x=point.x时,说明point在p1p2线段上
                nCross++; // 只统计单边交点
        }
        // 单边交点为偶数,点在多边形之外 ---
        return (nCross % 2 == 1);
    }

    /**
     * 返回一个点是否在一个多边形边界上
     *
     * @param mPoints 多边形坐标点列表
     * @param point   待判断点
     * @return true 点在多边形边上,false 点不在多边形边上。
     */
    public static boolean isPointInPolygonBoundary(List<LatLng> mPoints, LatLng point) {
        for (int i = 0; i < mPoints.size(); i++) {
            LatLng p1 = mPoints.get(i);
            LatLng p2 = mPoints.get((i + 1) % mPoints.size());
            // 取多边形任意一个边,做点point的水平延长线,求解与当前边的交点个数

            // point 在p1p2 底部 --> 无交点
            if (point.longitude < Math.min(p1.longitude, p2.longitude))
                continue;
            // point 在p1p2 顶部 --> 无交点
            if (point.longitude > Math.max(p1.longitude, p2.longitude))
                continue;

            // p1p2是水平线段,要么没有交点,要么有无限个交点
            if (p1.longitude == p2.longitude) {
                double minX = Math.min(p1.latitude, p2.latitude);
                double maxX = Math.max(p1.latitude, p2.latitude);
                // point在水平线段p1p2上,直接return true
                if ((point.longitude == p1.longitude) && (point.latitude >= minX && point.latitude <= maxX)) {
                    return true;
                }
            } else { // 求解交点
                double x = (point.longitude - p1.longitude) * (p2.latitude - p1.latitude) / (p2.longitude - p1.longitude) + p1.latitude;
                if (x == point.latitude) // 当x=point.x时,说明point在p1p2线段上
                    return true;
            }
        }
        return false;
    }

}

使用说明:只需要将SpatialRelationUtil这个工具类,复制到你的项目就可以直接使用,不用添加任何jar包。

好多人说不知道LatLngBounds类的具体实现,其实这是Google map包中的一个类,内部功能很简单,就是提供了一个构造器Builder可以不断的往里面添加经纬度点LatLng,不断计算更新Bounds范围和center中心点。

下面贴上整理后的LatLngBounds类,可以减去导入Google map包的麻烦

/**
 * 经纬度范围类
 *
 * 复写com.google.android.gms.maps.model.LatLngBounds中核心方法
 *
 * @author maple
 */
class LatLngBounds constructor(
        private val southwest: LatLng,// 左下角 点
        private val northeast: LatLng // 右上角 点
) {

    val center: LatLng
        get() {
            // 计算中心点纬度
            val centerLat = (this.southwest.latitude + this.northeast.latitude) / 2.0
            // 计算中心点经度
            val neLng = this.northeast.longitude // 右上角 经度
            val swLng: Double = this.southwest.longitude // 左下角 经度
            val centerLng: Double = if (swLng <= neLng) {
                (neLng + swLng) / 2.0
            } else {
                (neLng + 360.0 + swLng) / 2.0
            }
            return LatLng(centerLat, centerLng)
        }

    // 某个点是否在该范围内(包含边界)
    fun contains(point: LatLng): Boolean {
        return latContains(point.latitude) && this.lngContains(point.longitude)
    }

    // 某个纬度值是否在该范围内(包含边界)
    private fun latContains(lat: Double): Boolean {
        return this.southwest.latitude <= lat && lat <= this.northeast.latitude
    }

    // 某个经度值是否在该范围内(包含边界)
    private fun lngContains(lng: Double): Boolean {
        return if (this.southwest.longitude <= this.northeast.longitude) {
            this.southwest.longitude <= lng && lng <= this.northeast.longitude
        } else {
            this.southwest.longitude <= lng || lng <= this.northeast.longitude
        }
    }

    // 小数据量可以使用该方法,大数据量建议使用Builder中的include()
    fun including(point: LatLng): LatLngBounds {
        val swLat = Math.min(this.southwest.latitude, point.latitude)
        val neLat = Math.max(this.northeast.latitude, point.latitude)
        var neLng = this.northeast.longitude
        var swLng = this.southwest.longitude
        val pLng = point.longitude
        if (!this.lngContains(pLng)) {
            if (zza(swLng, pLng) < zzb(neLng, pLng)) {
                swLng = pLng
            } else {
                neLng = pLng
            }
        }
        return LatLngBounds(LatLng(swLat, swLng), LatLng(neLat, neLng))
    }

    /**
     * LatLngBounds生成器
     */
    class Builder {
        private var swLat = 1.0 / 0.0   // 左下角 纬度
        private var swLng = 0.0 / 0.0   // 左下角 经度
        private var neLat = -1.0 / 0.0  // 右上角 纬度
        private var neLng = 0.0 / 0.0   // 右上角 经度

        fun include(point: LatLng): Builder {
            this.swLat = Math.min(this.swLat, point.latitude)
            this.neLat = Math.max(this.neLat, point.latitude)
            val pLng = point.longitude
            if (java.lang.Double.isNaN(this.swLng)) {
                this.swLng = pLng
            } else {
                if (lngContains(pLng)) {
                    return this
                }
                if (zza(this.swLng, pLng) < zzb(this.neLng, pLng)) {
                    this.swLng = pLng
                    return this
                }
            }
            this.neLng = pLng
            return this
        }

        // 某个经度值是否在该范围内(包含边界)
        private fun lngContains(lng: Double): Boolean {
            return if (this.swLng <= this.neLng) {
                this.swLng <= lng && lng <= this.neLng
            } else {
                this.swLng <= lng || lng <= this.neLng
            }
        }

        fun build(): LatLngBounds {
            // Preconditions.checkState(!java.lang.Double.isNaN(this.swLng), "no included points")
            return LatLngBounds(LatLng(this.swLat, this.swLng), LatLng(this.neLat, this.neLng))
        }
    }

    companion object {
        fun builder(): Builder {
            return Builder()
        }

        // 前者 - 后者
        private fun zza(var0: Double, var2: Double): Double {
            return (var0 - var2 + 360.0) % 360.0
        }

        // 后者 - 前者
        private fun zzb(var0: Double, var2: Double): Double {
            return (var2 - var0 + 360.0) % 360.0
        }
    }

}

LatLng就是一个x,y的点类

 /**
     * 经纬度点
     */
    class LatLng(
            val latitude: Double,   // 纬度
            val longitude: Double   // 经度
    ) {
        override fun toString(): String {
            return "LatLng(latitude=$latitude, longitude=$longitude)"
        }
    }

注意??:LatLngBounds中including()方法  和 Builder中include()方法在具体实现上是相同,但是LatLngBounds每次都会new一个新的对象,所以不适合大批量数据时使用,e.g:在计算一个点比较多的Polygon的范围时,建议使用Builder中的include()方法。

效果展示:这个Demo展示判断事发地点是否在管辖区域内,也就是判断圆心是否在某一个基础区域内,如果在基础区域内,显示圆的半径(单位英里),如果不在基础区域给予提示:Point not in jurisdiction(事发点不在管辖范围内)。

本文转自:https://blog.csdn.net/shao941122/article/details/51504519

原文地址:https://www.cnblogs.com/nizuimeiabc1/p/11030885.html

时间: 2024-10-25 16:23:14

判断点是否在任意多边形内的相关文章

zoj 1081 Points Within 判断点是否在任意多边形内(模板)

题目来源: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=81 分析: 从p点出发做平行于x轴的射线 l. 求射线与 多边形 线段的交点数num, 若是偶数 , 该点 在外, 若为奇数, 该点在内. 注意: 两个特判, 1:   一个是 射线 l 与 多边形的边  重合 , 若该p点在 线段上, 返回1, 否则 交点 记为 0 个 2: 一个是 射线与 线段的交点 ,为线段的端点, 则我们 对线段的 较低交点 不计算. 代码

如何判断一个点是否在一个多边形内?

提示:对多边形进行分割,成为一个个三角形,判断点是否在三角形内. 一个非常有用的解析几何结论:如果P2(x1,y1),P2(x2,y2), P3(x3,y3)是平面上的3个点,那么三角形P1P2P3的面积等于下面绝对值的二分之一: | x1  y1  1 | | x2 y2  1 | = x1y2 + x3y1 + x2y3 –x3y2 – x2y1 – x1y3 | x3 y3  1 | 当且仅当点P3位于直线P1P2(有向直线P1->P2)的右侧时,该表达式的符号为正.这个公式可以在固定的时

判断点是否任意多边形内的2种方法

导入 判断触摸点是否在一个多边形的内部 方法 1.数学方法 这个方法的好处是任意平台都可以使用,不仅现于Android 算法: 求解通过该点的水平线与多边形各边的交点,单边交点为奇数,则成立 ok我们其实就是需要看这个点的单边射线与多边形的交点,代码实现如下: [java] view plaincopyprint? public boolean isInPolygon(Point point, Point[] points, int n) { int nCross = 0; for (int i

如何判断一个点是否在多边形内

原理 如何判断一个点在多边形内还是多边形外,最常见的方法就是射线法,原理就是,从点P开始,做一条任意的射线,如果射线与多边形边的交点个数为偶数个则表明点在多边形外,交点个数为奇数个时则表明点在多边形内.如果点在多边形内部时,无论如何画射线都会有交点,且为奇数个.如下图: 实现(C#) 1 public static bool InsidePolygon(List<Point> polygon, Point p) 2 { 3 if (polygon.Count <= 0) 4 return

判断一个点是否在多边形内

#转载自:http://blog.csdn.net/u011722133/article/details/52813374 在GIS(地理信息管理系统)/PCL(点云库)中,判断一个坐标是否在多边形内部是个经常要遇到的问题.乍听起来还挺复杂.根据W. Randolph Franklin 提出的PNPoly算法,只需区区几行代码就解决了这个问题 假设多边形的坐标存放在一个数组里,首先我们需要取得该数组在横坐标和纵坐标的最大值和最小值,根据这四个点算出一个四边型,首先判断目标坐标点是否在这个四边型之

POJ 1584 A Round Peg in a Ground Hole 判断凸多边形 点到线段距离 点在多边形内

首先判断是不是凸多边形 然后判断圆是否在凸多边形内 kuangbin的板子,但是有些地方不明白. 判断多边形不是凸多边形后,为什么用判断点是否在凸多边形内的模板交WA了,而用判断点是否在任意多边形内的模板A了 而且判断点是否在任意多边形的注释,返回值为什么又说是凸多边形~~~ POJ 1584 A Round Peg in a Ground Hole(判断凸多边形,点到线段距离,点在多边形内) #include <iostream> #include <cstdio> #inclu

POJ - 1584 A Round Peg in a Ground Hole(判断凸多边形,点到线段距离,点在多边形内)

http://poj.org/problem?id=1584 题意 按照顺时针或逆时针方向输入一个n边形的顶点坐标集,先判断这个n边形是否为凸包. 再给定一个圆形(圆心坐标和半径),判断这个圆是否完全在n边形内部. 分析 1.判断给出了多边形是不是凸多边形. 2.判断圆包含在凸多边形中:一定要保证圆心在凸多边形里面.然后判断圆心到每条线段的距离要大于等于半径.. #include <iostream> #include <stdio.h> #include <string.h

判断一个点是否在一个多边形里

“判断一个点是否在一个多边形里”,一开始以为是个挺难的问题,但Google了一下之后发现其实蛮简单,所用到的算法叫做“Ray-casting Algorithm”,中文应该叫“光线投射算法”,这是维基百科的描述:[维基百科] 简单地说可以这么判断:从这个点引出一根“射线”,与多边形的任意若干条边相交,累计相交的边的数目,如果是奇数,那么点就在多边形内,否则点就在多边形外. 如图,A点引一条射线,与多边形3条边相交,奇数,所以A点在多边形内,而从B点引一条射线,与多边形的2条边相交,偶数,所以B点

ZOJ 3720 Magnet Darts (计算几何,概率,判点是否在多边形内)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3720 题意: 在一个矩形区域投掷飞镖,因此飞镖只会落在整点上,投到每个点的得分是Ax+By.矩形区域里面有个多边形,如果飞镖投在多边形里面则得分,求最终的得分期望. 即:给定一个矩形内的所有整数点,判断这些点是否在一个多边形内 方法: 计算几何的判点是否在多边形内(几何模板),如果在,则令得分加(Ax+By)*以此点为中心边长为1的正方形面积 1 void solve()