point = Point(0.5, 0.5)
polygon = Polygon([(0, 0), (0, 1), (1, 1), (1, 0)])
print(polygon.contains(point))
判断某个点是否在某多边形内是常见的问题,比如某点是否在某个城市内、某城市位于哪个国家等。但地球是一个球面,对于比较小的多边形,采用平面的算法通常不会有问题。但是在某些情况下(尤其是多边形非常大的时候),直接应用平面上的算法会有问题。其中最常见的就是多边形横跨 180° 经线的场景。
本文将以 Python 作为示例代码,探索解决这种问题的方案。
使用模拟的数据可能不如使用真实数据更能显示其应用性。这里我选取的
之前的文章
曾经使用过的
Nuvel 板块模型
数据,作为测试的多边形。测试的点选择了位置上有代表性的 9 个城市。这些城市的坐标为:
>>> cities = {
... 'Beijing': (39.9075, 116.3880), 'Dubai': (25.2631, 55.2972),
... 'Honolulu': (22.3089, -157.8261), 'Johannesburg': (-26.1833, 27.9989),
... 'Mumbai': (19.0728, 72.8825), 'New York': (40.7144, -74.0060),
... 'Paris': (48.8583, 2.2944), 'Rio': (-22.9083, -43.1964),
... 'Sydney': (-33.9167, 150.8833)
... }
而板块数据,作者做了适当调整。原因在于其原数据和城市坐标表示西经的方式不同:板块数据使用大于 180 的值表示西经,而城市坐标采用负数表示。因此,我将板块数据中大于 180 的经度减去 360,使其与城市坐标一致。板块和城市的分布如下图。
板块和城市分布图
对于点与多边形的问题,目前主要使用的有两个 Python 包:matplotlib.path 和 Shapely。使用这两个包都能解决问题,接下来我将分别演示每一种的使用方法。我个人偏好于 Shapely,因为其处理图形关系方面功能更强大一些。
下面分别使用两个包测试阿拉伯板块(Arabian)与各城市的关系。使用 matplotlib.path 的代码如下:
>>> import numpy as np
>>> from matplotlib.path import Path
>>> arabian = np.genfromtxt('standard-plates/Arabian.txt', names=['lon', 'lat'],
... dtype=float, comments=':')
>>> polygon1 = Path([(lon, lat) for lon, lat in arabian])
>>> for city, coord in cities.items():
... if polygon1.contains_point([coord[1], coord[0]]):
... print(f'{city} is in Arabian plate!')
使用 Shapely 的代码如下:
>>> import numpy as np
>>> from shapely.geometry import Point
>>> from shapely.geometry.polygon import Polygon
>>> arabian = np.genfromtxt('standard-plates/Arabian.txt', names=['lon', 'lat'],
... dtype=float, comments=':')
>>> polygon2 = Polygon([(lon, lat) for lon, lat in arabian])
>>> for city, coord in cities.items():
... point = Point(coord[1], coord[0])
... if polygon2.contains(point):
... print(f'{city} is in Arabian plate!')
两份代码的执行结果都是打印出:
Dubai is in Arabian plate!
这是正确的。但别高兴得太早,如果把上述的阿拉伯板块(Arabian)替换为太平洋板块(Pacific),你就会发现问题了。两份代码都打印出:
Beijing is in Pacific plate!
Dubai is in Pacific plate!
Mumbai is in Pacific plate!
New York is in Pacific plate!
Paris is in Pacific plate!
这是因为,太平洋板块这个多边形横跨了 180 度经线。我们都知道,在大地坐标系中,东西经 180 度位于同一条经线,但这两个包应用的是平面算法。所以程序不认为这个板块的坐标是连续的。解决这个问题的方法有两个:一是应用传统制图学的方法,将多边形和点位投影到平面上,然后使用投影平面上的坐标进行位置关系计算;二是预处理跨 180 度经线的多边形,将其沿 180 度经线分割成小的区块,分别与点位计算位置关系。
有一个名为 pyproj 的包可以处理地图投影问题。但我采用了第二种方案。因为我担心地图投影有可能会将事情变得复杂:选择适合的地图投影和投影参数将是个麻烦。幸好本示例中需要分割的板块还不太多(只有 4 个),它们是:太平洋板块(Pacific)、澳大利亚板块(Australian)、北美洲板块(North American)和南极洲板块(Antarctic)。将它们沿 180 度经线分割后的图形如下。
分割后的 4 个板块
我已将本文使用的代码、分割前后的板块数据整理为一个压缩文件,你可以下载验证使用。最后使用分割后的边界重新测试:
>>> import json
>>> from shapely.geometry import Point
>>> from shapely.geometry.polygon import Polygon
>>> plates = {}
>>> for name in ['African', 'Antarctic', 'Arabian', 'Australian', 'Caribbean',
... 'Cocos', 'Eurasian', 'Indian', 'Juan', 'Nazca', 'North_Am',
... 'Pacific', 'Philippine', 'Scotia', 'South_Am']:
... with open(f'new/{name}.json') as j_f:
... plates[name] = json.load(j_f)
>>> boundaries = {}
>>> for name, info in plates.items():
... boundaries[name] = []
... for frag in info['fragments']:
... poly = Polygon(frag)
... boundaries[name].append(poly)
>>> for city, coord in cities.items():
... point = Point(coord[1], coord[0])
... for name, bound in boundaries.items():
... if any(frag.contains(point) for frag in bound):
... print(f'{city} is in {name}!')
以上代码的输出为:
Beijing is in Eurasian!
Dubai is in Arabian!
Honolulu is in Pacific!
Johannesburg is in African!
Mumbai is in Indian!
New York is in North_Am!
Paris is in Eurasian!
Rio is in South_Am!
Sydney is in Australian!
完美解决问题!
测试一个多边形是否完全包含另一个多边形
这在计算多边形是洞还是岛时很有用。
这个库不是高度优化的,但应该适合您对一般情况的需求。 如果速度不够快,请联系我,我会看看我们能做些什么。
npm install 2d-plolygon-contains-polygon
var a = [
[ 0 , 0 ] ,
[ 100 , 0 ] ,
[ 100 , 100 ] ,
[ 0 , 100 ]
var b = [
[ 20 , 20 ] ,
[ 80 , 20 ] ,
[ 80 , 80 ] ,
[ 20 , 80 ]
console . log ( contains ( a , b ) ) ;
// output: true
api表面
包含( container , contained )
contain
改进多边形
改进 java.awt.Polygon contains() 方法的多边形类。
java.awt.Polygon 中的 contains() 方法可能不会为您期望的所有点返回 true。 示例:xArray = {0, 5, 10, 15, 15, 0},yArray = {0, 5, 3, 10, 0, 0}。 对于标准的 Polygon 类, contains(5,5), contains(15,10), contains(15, 0) 都将返回 false,尽管这些点位于多边形上。 如果您使用 Matlab 的 inpolygon 函数,您将对所有这些函数都适用。
原因与“内幕”的定义有关。 当且仅当,一个点被认为位于 Shape 内:
它完全位于 Shape 边界内或
它正好位于 Shape 边界上,并且在 X 方向增加的点紧邻的空间完全在边界内
或者它正好位于水
#!/usr/bin/python3.4
# -*- coding:utf-8 -*-
def isPointinPolygon(point, rangelist): #[[0,0],[1,1],[0,1],[0,0]] [1,0.8]
# 判断是否在外包矩形内,如果不在,直接返回false
lnglist = []
latlist = []
for i in range(len(rangelist)-1):
lnglist.append(rangelist[i][0])
它们是反比关系:A包含B,B在A内.>>> A.contains(B)True>>> B.within(A)True+----------------------------------+| || +----------+ || | ...
文章目录构建集合图形以及获取集合图形点信息多边形显示多边形分割Polygon 被 MultiLineString 切割merge 多个多边形从Python形状多边形中提取点/坐标检查地理点是否在Python中的多边形内或外Python中用shapely做(1)生成二个多边形区域,计算想交的面积(2)生成一个点与一多边通过点缓冲来构建一个缓冲圆一般对象和方法PointsLineStringLineRingsPolygonsCollectionsCollections of PointsCollections
要判断点是否在多边形的内部,适用于任意多边形的方法最常用的就是射线法,即以要判断的点向左或者向右作水平射线,与多边形交点的个数为奇数个时则在多边形的内部,若为偶数个交点时则在多边形的外部,其中0个交点也为偶数个交点。
依据该原理,于是有了第一种实现
/// <summary>
/// 判断点是否在多边形内.
///...
libgdx中Math封装了Polygon这个类,它是由多个定点进行描述实现的,在进行物体间的碰撞时,物体轮廓有时候是不规则的,这时候可以用一个多边形勾勒出其大概的轮廓,对其进行模拟。
Polygon内部自带是否包含点contains这个函数,通过这个函数我们可以判断两个多变行是否碰撞,即检测两个多边形的每个点是否在另一个多边形中。
检测代码:
1 public static ...
C++版本判断点是否落入多边形内原理讲解及代码实现1.判断点落在多边形内原理1.1面积法1.2角度法1.3射线法2.判断点落在多边形内C++代码实现
1.判断点落在多边形内原理
1.1面积法
判断目标点与多边形的每条边组成的三角形面积和是否等于该多边形,相等则在多边形内部。
1.2角度法
判断目标点与所有边的夹角和是否为360度,为360度则在多边形内部。
1.3射线法
从目标点出发引一条射线,看这条射线和多边形所有边的交点数目:
(1)如果有奇数个交点,则说明在内部;
(2)如果有偶数个交点,则说明在外
java/c# 判断点是否在多边形区域内2012年06月29日⁄ 综合⁄ 共 1547字 ⁄ 字号中大⁄评论关闭最近帮别人解决了一个问题,如何判断一个坐标点,是否在多边形区域内(二维)。在网上搜索了一圈,都是自己写代码,有多种算法,分凸多边形、凹多边形,总之是麻烦。继续搜索,了解到 Java/dotnet 自带的类库中,都有现成的类函数,可以解决这个问题。考虑到了解的人不多,特将相关知识共...
public Polygon(int[] xpoints,int[] ypoints,int npoints)Parameters:xpoints - an array of X coordinates 所有点的x坐标ypoints - an array of Y coordinates 所有点的y坐标npoints - .
(1)面积和判别法:判断目标点与多边形的每条边组成的三角形面积和是否等于该多边形,相等则在多边形内部。
(2)夹角和判别法:判断目标点与所有边的夹角和是否为360度,为360度则在多边形内部。
(3)引射线法:从目标点出发引一条射线,看这条射线和多边形所有边的交点数目。如果有奇数个交点,则说明在内部,如果有偶数个交点,则说明在外部。
//判断一个点是否在多边形内
public static boolean isPtInPoly(Point2D.Double point, List<Point2D.Double> pts) {
int N = pts.size();
boolean boundOrVertex = true; //如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
int intersectCount = 0;//cross poi...