Python求凸包及多边形面积教程


Posted in Python onApril 12, 2020

一般有两种算法来计算平面上给定n个点的凸包:Graham扫描法(Graham's scan),时间复杂度为O(nlgn);Jarvis步进法(Jarvis march),时间复杂度为O(nh),其中h为凸包顶点的个数。这两种算法都按逆时针方向输出凸包顶点。

Graham扫描法

用一个栈来解决凸包问题,点集Q中每个点都会进栈一次,不符合条件的点会被弹出,算法终止时,栈中的点就是凸包的顶点(逆时针顺序在边界上)。

算法步骤如下图:

Python求凸包及多边形面积教程

Python求凸包及多边形面积教程

Python求凸包及多边形面积教程

Python求凸包及多边形面积教程

Python求凸包及多边形面积教程

Python求凸包及多边形面积教程

import sys
import math
import time
import random

#获取基准点的下标,基准点是p[k]
def get_leftbottompoint(p):
 k = 0
 for i in range(1, len(p)):
  if p[i][1] < p[k][1] or (p[i][1] == p[k][1] and p[i][0] < p[k][0]):
   k = i
 return k

#叉乘计算方法
def multiply(p1, p2, p0):
 return (p1[0] - p0[0]) * (p2[1] - p0[1]) - (p2[0] - p0[0]) * (p1[1] - p0[1])

#获取极角,通过求反正切得出,考虑pi/2的情况
def get_arc(p1, p0):
 # 兼容sort_points_tan的考虑
 if (p1[0] - p0[0]) == 0:
  if ((p1[1] - p0[1])) == 0:
   return -1;
  else:
   return math.pi / 2
 tan = float((p1[1] - p0[1])) / float((p1[0] - p0[0]))
 arc = math.atan(tan)
 if arc >= 0:
  return arc
 else:
  return math.pi + arc

#对极角进行排序,排序结果list不包含基准点
def sort_points_tan(p, pk):
 p2 = []
 for i in range(0, len(p)):
  p2.append({"index": i, "arc": get_arc(p[i], pk)})
 #print('排序前:',p2)
 p2.sort(key=lambda k: (k.get('arc')))
 #print('排序后:',p2)
 p_out = []
 for i in range(0, len(p2)):
  p_out.append(p[p2[i]["index"]])
 return p_out

def convex_hull(p):
 p=list(set(p))
 #print('全部点:',p)
 k = get_leftbottompoint(p)
 pk = p[k]
 p.remove(p[k])
 #print('排序前去除基准点的所有点:',p,'基准点:',pk)

 p_sort = sort_points_tan(p, pk) #按与基准点连线和x轴正向的夹角排序后的点坐标
 #print('其余点与基准点夹角排序:',p_sort)
 p_result = [pk,p_sort[0]]

 top = 2
 for i in range(1, len(p_sort)):
  #####################################
  #叉乘为正,向前递归删点;叉乘为负,序列追加新点
  while(multiply(p_result[-2], p_sort[i],p_result[-1]) > 0):
   p_result.pop()
  p_result.append(p_sort[i]) 
 return p_result#测试
if __name__ == '__main__':
 pass
 test_data = [(220, -100), (0,0), (-40, -170), (240, 50), (-160, 150), (-210, -150)]
 print(test_data)

 result = convex_hull(test_data)
 print(result)
 t=0

import matplotlib.pyplot as plt
x1=[]
y1=[]
for i in range(len(test_data)):
 ri=test_data[i]
 #print(ri)
 x1.append(ri[0])
 y1.append(ri[1])

plt.plot(x1,y1,linestyle=' ',marker='.')


xx=[]
yy=[]
for i in range(len(result)):
 ri=result[i]
 #print(ri)
 xx.append(ri[0])
 yy.append(ri[1])

plt.plot(xx,yy,linestyle=' ',marker='*')

Python求凸包及多边形面积教程

计算多边形面积

(1)顺时针给定构成凸包的n个点坐标,叉乘法求多边形面积:

Python求凸包及多边形面积教程

def GetAreaOfPolyGonbyVector(points):
 # 基于向量叉乘计算多边形面积
 area = 0
 if(len(points)<3):
  raise Exception("error")

 for i in range(0,len(points)-1):
  p1 = points[i]
  p2 = points[i + 1]

  triArea = (p1[0]*p2[1] - p2[0]*p1[1])/2
  #print(triArea)
  area += triArea

 fn=(points[-1][0]*points[0][1]-points[0][0]*points[-1][1])/2
 #print(fn)
 return abs(area+fn)

points = []
x = [1,3,2]
y = [1,2,2] 
#[(1,1),(3,1),(5,3),(3,5),(1,3)] 
# x=[1,3,5,3,1]
# y=[1,1,3,5,3]
for index in range(len(x)):
 points.append((x[index],y[index]))
area = GetAreaOfPolyGonbyVector(points)
print(area)
#print(math.ceil(area))

(2)顺时针给定构成凸包的n个点经纬度坐标,先将经纬度坐标转化成凸多边形的边的经纬度距离,利用海伦公式求多边形面积:

from geopy.distance import vincenty
import math
def HeronGetAreaOfPolyGonbyVector(points):
 # 基于海伦公式计算多边形面积
 area = 0
 if(len(points)<3):
  raise Exception("error")

 pb=((points[-1][0]+points[0][0])/2,(points[-1][1]+points[0][1])/2) #基准点选为第一个点和最后一个点连线边上的中点

 for i in range(0,len(points)-1):
  p1 = points[i]
  p2 = points[i + 1]

  db1 = vincenty(pb,p1).meters #根据维度转化成经纬度距离
  d12 = vincenty(p1,p2).meters
  d2b = vincenty(p2,pb).meters
  #print(db1,d12,d2b)

  hc = (db1+d12+d2b)/2 #db1是基准点和p1的距离,d12是p1和p2的距离,d2b是p2和基准点距离
  #print(hc, hc-db1, hc-d12, hc-d2b)
  triArea = math.sqrt(hc*(hc-db1)*(hc-d12)*(hc-d2b)) 
  #print(triArea)
  area += triArea

 return area


points = []
x = [1,3,2]
y = [1,2,2] 
#[(1,1),(3,1),(5,3),(3,5),(1,3)] 
# x=[1,3,5,3,1]
# y=[1,1,3,5,3]
for index in range(len(x)):
 points.append((x[index],y[index]))

area = HeronGetAreaOfPolyGonbyVector(points)
print(area)
#print(math.ceil(area))

Graham程序原理

(1)基准点的确认原则:

有唯一的某个点纵坐标最小,该点为基准点;

不止一个点的纵坐标最小,选这些点里最靠左的为基准点

(2)计算叉乘【后续利用叉乘正负判断夹角是否大于180o】:

Python求凸包及多边形面积教程

(3)获取极角,通过求反正切得出:

若横纵坐标都相等(两点相同),返回-1;

若横坐标相等/纵坐标不相等(两点连线垂直y轴),返回 Python求凸包及多边形面积教程

Python求凸包及多边形面积教程

(4)对极角进行排序,排序结果list不包含基准点:

p2=[{"index":0, "arc":get_arc(p[0],p[k])},
 {"index":1, "arc":get_arc(p[1],p[k])},
 ···
 {"index":k-1, "arc":get_arc(p[k-1],p[k])},
 {"index":k+1, "arc":get_arc(p[k+1],p[k])},
 ···
 {"index":n, "arc":get_arc(p[n],p[k])}]
#get_arc(p[0],p[k])即获得p[0]点与基准点p[k]连线的极角(与x轴正向夹角)
#根据p2的“arc”键的值从小到大排序,最后输出按该角度值排序对应顺序的各个点

(5)逆时针确定凸多边形:

Python求凸包及多边形面积教程

主要是找角度是否大于180o——差乘正负——点进出栈顺序三者关系

Python求凸包及多边形面积教程

...一直遍历到最后一个点...一直遍历到最后一个点

规律:叉乘>0,夹角小于180o,递归向前删点;叉乘<0,夹角大于180o,不删点,加入新点,向后遍历叉乘>0,夹角小于180o,递归向前删点;叉乘<0,夹角大于180o,不删点,加入新点,向后遍历

注意:(a)上述给非基准点按极角从到大小排号时,有两个及以上点“和基准点连线构成的极角”相等时,这些点的排号挨着但是没有固定顺序,这点并不影响算法给出凸包的准确性。(b)对排号最后的一个点,扫描算法里没有任何删除该点的机制,但是这点也不影响算法给出凸包的准确性。(c)上述程序需要额外加入,判断结束栈内点数小于3和筛选凸包前点数小于3,不能计算多边形面积的情况,可以直接给这种情况赋值0返回。

以上这篇Python求凸包及多边形面积教程就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python常用内置函数总结
Feb 08 Python
python转换字符串为摩尔斯电码的方法
Jul 06 Python
简单介绍Python中的几种数据类型
Jan 02 Python
深入讲解Java编程中类的生命周期
Feb 05 Python
python实现简单中文词频统计示例
Nov 08 Python
python实现机器人行走效果
Jan 29 Python
python将回车作为输入内容的实例
Jun 23 Python
Flask框架Flask-Login用法分析
Jul 23 Python
Pandas过滤dataframe中包含特定字符串的数据方法
Nov 07 Python
详解Python3网络爬虫(二):利用urllib.urlopen向有道翻译发送数据获得翻译结果
May 07 Python
Python 多个图同时在不同窗口显示的实现方法
Jul 07 Python
python实现的发邮件功能示例
Sep 11 Python
python实现人脸签到系统
Apr 13 #Python
python实现IOU计算案例
Apr 12 #Python
python 已知平行四边形三个点,求第四个点的案例
Apr 12 #Python
python 已知三条边求三角形的角度案例
Apr 12 #Python
python实现输入三角形边长自动作图求面积案例
Apr 12 #Python
Python3如何判断三角形的类型
Apr 12 #Python
Python判断三段线能否构成三角形的代码
Apr 12 #Python
You might like
codeigniter数据库操作函数汇总
2014/06/12 PHP
变量在 PHP7 内部的实现(一)
2015/12/21 PHP
PHP内存缓存功能memcached示例
2016/10/19 PHP
JS版网站风格切换实例代码
2008/10/06 Javascript
JavaScript中Object和Function的关系小结
2009/09/26 Javascript
Javascript表达式中连续的 &amp;&amp; 和 || 之赋值区别
2010/10/17 Javascript
jQuery1.6 类型判断实现代码
2011/09/01 Javascript
JS重要知识点小结
2011/11/06 Javascript
Ext中下拉列表ComboBox组件store数据格式用法介绍
2013/07/15 Javascript
js点击出现悬浮窗效果不使用JQuery插件
2014/01/20 Javascript
javascript引用类型指针的工作方式
2015/04/13 Javascript
实例详解JavaScript获取链接参数的方法
2016/01/01 Javascript
基于Bootstrap实现图片轮播效果
2016/05/22 Javascript
JavaScript使用delete删除数组元素用法示例【数组长度不变】
2017/01/17 Javascript
Nodejs实现短信验证码功能
2017/02/09 NodeJs
浅谈Angular2 模块懒加载的方法
2017/10/04 Javascript
详解webpack提取第三方库的正确姿势
2017/12/22 Javascript
layuiAdmin循环遍历展示商品图片列表的方法
2019/09/16 Javascript
详解Python2.x中对Unicode编码的使用
2015/04/03 Python
Python中__name__的使用实例
2015/04/14 Python
Python中的rjust()方法使用详解
2015/05/19 Python
Python编程中使用Pillow来处理图像的基础教程
2015/11/20 Python
Python字符串特性及常用字符串方法的简单笔记
2016/01/04 Python
python实现按行切分文本文件的方法
2016/04/18 Python
Python多线程原理与用法详解
2018/08/20 Python
python中的不可变数据类型与可变数据类型详解
2018/09/16 Python
Python实现统计英文文章词频的方法分析
2019/01/28 Python
对PyQt5中树结构的实现方法详解
2019/06/17 Python
Python matplotlib以日期为x轴作图代码实例
2019/11/22 Python
在Pytorch中计算自己模型的FLOPs方式
2019/12/30 Python
Python json读写方式和字典相互转化
2020/04/18 Python
护理专业应届毕业生推荐信
2013/11/15 职场文书
《诺贝尔》教学反思
2014/02/17 职场文书
法律系毕业生求职信
2014/05/28 职场文书
中考学习决心书
2015/02/04 职场文书
个人工作总结(管理人员)范文
2019/08/13 职场文书