点集p的凸包是指一个最小凸多边形(内角均小于180°),满足p中的点或者在多边形边上或者在其内
下图中的红色线段表示的多边形就是点集p={p0,p1,p2,p3,…………,p12}的凸包
Jarvis march(包裹法)
Graham Scan(扫描法)
Divide and conquer(分治法)
Divide and conquer(分治法)
这里只讲解最简单常用的Jarvis march(包裹法)
这种算法利用了凸包的一个特性:整个凸包都在任意一条边的一侧
基本思想如下:
点集中任意一点与p0的连线与x轴的夹角为alfa,选择使得夹角alfa最小的点为p1,此时点集中的所有点都在p0p1连线的同侧,将p1放入凸包点集中convexPoints=[p0,p1],并从点集p中删除点p1
alfa=arctan(dy/dx)
alfa=arctan(dy/dx)+pi
alfa=arctan(dy/dx)+pi
alfa=arctan(dy/dx)+2pi
def getAlfa(startPoint, endPoint):
dx = endPoint[0] - startPoint[0]
dy = endPoint[1] - startPoint[1]
if dx == 0:
if dy > 0:
return np.pi / 2
elif dy < 0:
return 3 * np.pi / 2
else:
return np.pi
alfa = np.arctan(dy / dx)
if dx < 0:
alfa += np.pi
elif dy < 0:
alfa += np.pi * 2
return alfa
dlfa=abs(alfa2-alfa1)
如果dalfa大于pi,由于凸多边形的内角小于180°,因此此时还需要用dalfa=2pi-dalfa
基于上述算法,用gda库计算点SHP文件的凸包
from osgeo import ogr, gdalconst, osr
import numpy as np
import pandas as pd
import os
def getAlfa(startPoint, endPoint):
dx = endPoint[0] - startPoint[0]
dy = endPoint[1] - startPoint[1]
if dx == 0:
if dy > 0:
return np.pi / 2
elif dy < 0:
return 3 * np.pi / 2
else:
return np.pi
alfa = np.arctan(dy / dx)
if dx < 0:
alfa += np.pi
elif dy < 0:
alfa += np.pi * 2
return alfa
def convexHull(xys: np.array):
convexPoints = []
ymin = np.min(xys, axis=0)[1]
index = np.where(xys[:, 1] == ymin)[0][0]
convexPoints.append(xys[index, :])
n = 1
while True:
if n == 1:
startPoint = convexPoints[0]
minAlfa = 0
index = 0
for i in range(xys.shape[0]):
endPoint = xys[i, :]
alfa = getAlfa(startPoint, endPoint)
if i == 0:
minAlfa = alfa
index = 0
else:
if alfa < minAlfa:
minAlfa = alfa
index = i
convexPoints.append(xys[index, :])
xys = np.delete(xys, index, axis=0)
n += 1
else:
startPoint = convexPoints[-1]
previousPoint = convexPoints[-2]
alfa0 = getAlfa(startPoint, previousPoint)
maxAlfa = 0
index = 0
for i in range(xys.shape[0]):
nextPoint = xys[i, :]
alfa1 = getAlfa(startPoint, nextPoint)
dalfa = (alfa0 - alfa1) if alfa0 > alfa1 else (alfa1 - alfa0)
if dalfa > np.pi:
dalfa = np.pi * 2 - dalfa
if i == 0:
maxAlfa = dalfa
index = 0
else:
if dalfa > maxAlfa:
maxAlfa = dalfa
index = i
convexPoints.append(xys[index, :])
xys = np.delete(xys, index, axis=0)
n += 1
# 判断首位是否重合
firstPoint = convexPoints[0]
lastPoint = convexPoints[-1]
if firstPoint[0] == lastPoint[0] and firstPoint[1] == lastPoint[1]:
break
wktPolygon = ""
for i in range(n):
x = convexPoints[i][0]
y = convexPoints[i][1]
wktPolygon = '{} {},'.format(x, y) + wktPolygon
wktPolygon = wktPolygon[0:-1]
wktPolygon = "POLYGON(({}))".format(wktPolygon)
return wktPolygon
if __name__ == "__main__":
ogr.RegisterAll()
ds = ogr.Open("./point.shp", gdalconst.GA_ReadOnly)
oLay = ogr.DataSource.GetLayer(ds, 0)
ogr.Layer.ResetReading(oLay)
xys = []
while True:
oFea = ogr.Layer.GetNextFeature(oLay)
if oFea == None:
break
oPoi = ogr.Feature.GetGeometryRef(oFea)
x = ogr.Geometry.GetX(oPoi)
y = ogr.Geometry.GetY(oPoi)
xys.append([x, y])
xys = np.array(xys)
wktPolygon = convexHull(xys)
driver = ogr.GetDriverByName("ESRI Shapefile")
convexds = ogr.Driver.CreateDataSource(driver, "convexHull.shp")
srs = ogr.Layer.GetSpatialRef(oLay)
convexLay = ogr.DataSource.CreateLayer(convexds, "convexhull", srs, ogr.wkbPolygon)
labelField = ogr.FieldDefn("label", ogr.OFTInteger)
ogr.Layer.CreateField(convexLay, labelField)
convexFea = ogr.Feature(ogr.Layer.GetLayerDefn(convexLay))
ogr.Feature.SetField(convexFea,"label",1)
convexPolygon=ogr.CreateGeometryFromWkt(wktPolygon)
ogr.Feature.SetGeometry(convexFea,convexPolygon)
ogr.Layer.CreateFeature(convexLay,convexFea)
ogr.DataSource.Destroy(convexds)
ogr.DataSource.Destroy(ds)
print('ok!')
结算结果,如下图所示: