前言
本人最近参与了某地区的农网升级改造项目,项目前期,我们的设计人员到现场进行了实地勘测,并在Google卫星图影像上绘制出了配网线路的设计图,如图1所示为配网线路设计图示例。
根据需求现在需要对整个项目的工程量做一个估计,为了得到这个估计我们就需要知道400V/220V线路上各有多少转角杆、直线杆以及沿墙等。由于整个项目包含的台区数量较多,如果靠人工手动地对各个类型的电杆进行统计速度较慢、效率低且工作量庞大。针对这一问题,我专门设计了一个算法分别对400V/220V线路上的转角杆、直线杆以及沿墙进行自动统计,利用Python语言对其进行了实现。
400V/220V线路转角杆数量统计
from pykml import parser
import math
# 文件打开
KML_FILE = 'data.kml'
with open(KML_FILE, 'r', encoding="utf-8") as f:
kml = parser.parse(f).getroot()
# 线上的“转角”、“非沿墙”点个数统计
color = 'ffff0000'
k_total = 0
for each in kml.Document.Folder.Placemark: # 遍历所有的Placemark
if each.name == '': # 选择线
if each.Style.LineStyle.color == color: # 选择线颜色,ffff0000蓝色,ff00ff00绿色,ff0000ff红色
k1 = 0 # 起始点、终止点统计
k = 0 # 中间的统计
# 将字符串坐标转换为列表
coo = str(each.LineString.coordinates) # 线坐标
coo = coo.split(' ')
# 去除列表空元素
coo = [x for x in coo if x != '']
# 将列表中的字符串元素更改为列表,即双重列表
coo_new = []
for i in coo:
i = i.split(',')
i = [ float(x) for x in i ]
coo_new.append(i)
print(coo_new)
# 判断起始点、终止点是否为“非沿墙”
num = len(coo_new)
for each2 in kml.Document.Folder.Placemark: # 遍历所有的Placemark
if each2.name != '': # 选择点
if '沿墙' not in str(each2.name):
coo_point = str(each2.Point.coordinates).split(',') # 将“点”的字符串坐标抓换为列表坐标
coo_point = [ float(x) for x in coo_point ] # 将列表中的字符串元素更改为数字
# 统计线上“点”个数
if coo_new[0][0] - 0.00002 < coo_point[0] < coo_new[0][0] + 0.00002:
if coo_new[0][1] - 0.00002 < coo_point[1] < coo_new[0][1] + 0.00002:
k1 = k1 + 1
if coo_new[num-1][0] - 0.00002 < coo_point[0] < coo_new[num-1][0] + 0.00002:
if coo_new[num-1][1] - 0.00002 < coo_point[1] < coo_new[num-1][1] + 0.00002:
k1 = k1 + 1
# 判断中间点是否为“转角”、“非沿墙”
# 计算夹角
for i in range(1,num-1):
x1 = coo_new[i-1][0]
y1 = coo_new[i-1][1]
x2 = coo_new[i][0]
y2 = coo_new[i][1]
x3 = coo_new[i+1][0]
y3 = coo_new[i+1][1]
dx1 = x1 - x2
dy1 = y1 - y2
dx2 = x3 - x2
dy2 = y3 - y2
x_max = x2 + 0.00002
x_min = x2 - 0.00002
y_max = y2 + 0.00002
y_min = y2 - 0.00002
angle1 = math.atan2(dy1, dx1)
angle1 = int(angle1 * 180/math.pi)
angle2 = math.atan2(dy2, dx2)
angle2 = int(angle2 * 180/math.pi)
if angle1*angle2 >= 0:
included_angle = abs(angle1-angle2)
else:
included_angle = abs(angle1) + abs(angle2)
if included_angle > 180:
included_angle = 360 - included_angle
# 夹角判断,若夹角小于175°,则该点为转角杆
if included_angle <= 175:
# 判断该中间点是否为“非沿墙”
for each1 in kml.Document.Folder.Placemark: # 遍历所有的Placemark
if each1.name != '': # 选择点
if '沿墙' not in str(each1.name):
coo_point = str(each1.Point.coordinates).split(',') # 将“点”的字符串坐标抓换为列表坐标
coo_point = [ float(x) for x in coo_point ] # 将列表中的字符串元素更改为数字
# 统计线上“点”个数
if x_min < coo_point[0] < x_max:
if y_min < coo_point[1] < y_max:
k = k + 1
k = k + k1 # 中间点统计结果加上起始点、终止点统计结果
k_total = k_total + k
print(k)
print('总点数 %s' %k_total) # 每种颜色线上的总的“点”个数
在本项目中,所有的400V四线线路均用蓝色线表示,220V两线线路均用绿色线表示,在如上程序中,若将color赋值为蓝色‘ffff0000’,即统计的是400V线路转角杆数量,若将color赋值为绿色‘ff00ff00’,即统计的是220V线路转角杆数量。
400V/220V线路直线杆数量统计
# 线上的“直线”、“非沿墙”点个数统计
color = 'ffff0000'
k_total = 0
for each in kml.Document.Folder.Placemark: # 遍历所有的Placemark
if each.name == '': # 选择线
if each.Style.LineStyle.color == 'ffff0000': # 选择线颜色,ffff0000蓝色,ff00ff00绿色,ff0000ff红色
k = 0 # 中间的统计
# 将字符串坐标转换为列表
coo = str(each.LineString.coordinates) # 线坐标
coo = coo.split(' ')
# 去除列表空元素
coo = [x for x in coo if x != '']
# 将列表中的字符串元素更改为列表,即双重列表
coo_new = []
for i in coo:
i = i.split(',')
i = [ float(x) for x in i ]
coo_new.append(i)
print(coo_new)
# 判断中间点是否为“直线”、“非沿墙”
# 计算夹角
num = len(coo_new)
for i in range(1,num-1):
x1 = coo_new[i-1][0]
y1 = coo_new[i-1][1]
x2 = coo_new[i][0]
y2 = coo_new[i][1]
x3 = coo_new[i+1][0]
y3 = coo_new[i+1][1]
dx1 = x1 - x2
dy1 = y1 - y2
dx2 = x3 - x2
dy2 = y3 - y2
x_max = x2 + 0.00002
x_min = x2 - 0.00002
y_max = y2 + 0.00002
y_min = y2 - 0.00002
angle1 = math.atan2(dy1, dx1)
angle1 = int(angle1 * 180/math.pi)
angle2 = math.atan2(dy2, dx2)
angle2 = int(angle2 * 180/math.pi)
if angle1*angle2 >= 0:
included_angle = abs(angle1-angle2)
else:
included_angle = abs(angle1) + abs(angle2)
if included_angle > 180:
included_angle = 360 - included_angle
# 夹角判断,若夹角大于175°,则该点为直线杆
if included_angle > 175:
# 判断该中间点是否为“非沿墙”
for each1 in kml.Document.Folder.Placemark: # 遍历所有的Placemark
if each1.name != '': # 选择点
if '沿墙' not in str(each1.name):
coo_point = str(each1.Point.coordinates).split(',') # 将“点”的字符串坐标抓换为列表坐标
coo_point = [ float(x) for x in coo_point ] # 将列表中的字符串元素更改为数字
# 统计线上“点”个数
if x_min < coo_point[0] < x_max:
if y_min < coo_point[1] < y_max:
k = k + 1
k_total = k_total + k
print(k)
print('总点数 %s' %k_total) # 每种颜色线上的总的“点”个数
在如上程序中,若将color赋值为蓝色‘ffff0000’,即统计的是400V线路直线杆数量,若将color赋值为绿色‘ff00ff00’,即统计的是220V线路直线杆数量。
400V/220V线路沿墙数量统计
# 线上的“沿墙”点个数统计
k_total = 0
for each in kml.Document.Folder.Placemark: # 遍历所有的Placemark
if each.name == '': # 选择线
if each.Style.LineStyle.color == 'ffff0000': # 选择线颜色,ffff0000蓝色,ff00ff00绿色,ff0000ff红色
k = 0 # 中间的统计
# 将字符串坐标转换为列表
coo = str(each.LineString.coordinates) # 线坐标
coo = coo.split(' ')
# 去除列表空元素
coo = [x for x in coo if x != '']
# 将列表中的字符串元素更改为列表,即双重列表
coo_new = []
for i in coo:
i = i.split(',')
i = [ float(x) for x in i ]
coo_new.append(i)
print(coo_new)
# 判断点是否为“沿墙”
num = len(coo_new)
for i in range(num):
for each1 in kml.Document.Folder.Placemark: # 遍历所有的Placemark
if each1.name != '': # 选择点
if '沿墙' in str(each1.name):
coo_point = str(each1.Point.coordinates).split(',') # 将“点”的字符串坐标抓换为列表坐标
coo_point = [ float(x) for x in coo_point ] # 将列表中的字符串元素更改为数字
# 统计线上“点”个数
if (coo_new[i][0] - 0.00002) < coo_point[0] < (coo_new[i][0] + 0.00002):
if (coo_new[i][1]- 0.00002) < coo_point[1] < (coo_new[i][1] + 0.00002):
k = k + 1
k_total = k_total + k
print(k)
print('总点数 %s' %k_total) # 每种颜色线上的总的“点”个数
在如上程序中,若将color赋值为蓝色‘ffff0000’,即统计的是400V线路上沿墙点数量,若将color赋值为绿色‘ff00ff00’,即统计的是220V线路上沿墙点数量。