Commit b5488cdd authored by xiongchao's avatar xiongchao

提交新版自动化接边,吐出三份报表

parent 89cd51f0
# edge_mathch
1. 高精地图数据图幅边框接边工具使用方法
本工具通过命令行参数制定输入和输出参数, 参数含义如下:
1) 数据库连接字符串,如"PG:dbname='test3' host='localhost' port='5432' user='postgres' password='19931018'"
2) 需要接边的图幅id文件,如"C:\Users\熊\Desktop\edge_match_file\mesh_id.txt"
3) 图幅框文件,如"C:\Users\熊\Desktop\edge_match_file\mesh_grid.gpkg"
本工具通过命令行参数制定输入和输出参数, 参数含义如下:
1) 数据库连接字符串,如"PG:dbname='test3' host='localhost' port='5432' user='postgres' password='19931018'"
2) 需要接边的图幅id文件,如"C:\Users\熊\Desktop\edge_match_file\mesh_id.txt"
3) 图幅框文件,如"C:\Users\熊\Desktop\edge_match_file\mesh_grid.gpkg"
4) 接边结果报表保存文件夹路径,如"C:\Users\熊\Desktop\edge_match_file"
2. 示例如下:
lane_match "PG:dbname='test3' host='localhost' port='5432' user='postgres' password='19931018'" "C:\Users\熊\Desktop\edge_match_file\mesh_id.txt" "C:\Users\熊\Desktop\edge_match_file\mesh_grid.gpkg"
main "PG:dbname='test2' host='localhost' port='5432' user='postgres' password='19931018'" "C:\Users\熊\Desktop\edge_match_file\mesh_ids.txt" "C:\Users\熊\Desktop\edge_match_file\mesh_grid.gpkg" "C:\Users\熊\Desktop\edge_match_file"
3. 备注
3.1 生成的未接边文件名为:未接边信息.txt,其在图幅框文件的同一目录下
3.2 在图幅框文件目录下会生成一个新的图幅框文件,其中的图幅框仅包含“图幅id文件”中的图幅
\ No newline at end of file
3.1 生成的未接边文件名为:未接边信息.csv,其在输入的报表文件夹下
3.2 生成的已接边信息.txt和不需要接边信息.txt是给研发人员使用,作业员无需关注
\ No newline at end of file
# _*_ coding: utf-8 _*_ #
# @Time: 2020/4/22 17:04
# @Author: XiongChao
import shutil
import os
from smoothing.savitsky_golay_smoothing import automatic_smoothing
from smoothing.polynomial_fitting import get_coefficient_vector, poly_fitting_parameter
from smoothing.rotation_and_translation_of_coordinate import positive_transformation_point, \
negative_transformation_point, positive_transformation
from util.data_interpolation import interpolation_between_two_point
from util.improved_douglas_peuker import DouglasPeuker
from util.common import *
from edge_match_src import *
class BaseConnectMesh:
def __init__(self, connect_postgresql, sheet_designation_path, mesh_box_path):
self.connect_postgresql = connect_postgresql
self.sheet_designation_path = sheet_designation_path
self.mesh_box_path = mesh_box_path
self.map_data_source = ogr.Open(self.connect_postgresql, 1)
self.mesh_box_data_source = ogr.Open(self.mesh_box_path, 1)
def is_execute(self):
# 判断是否执行后面的接边逻辑
if self.map_data_source is None or self.mesh_box_data_source is None:
return False
return True
def get_sheet_designations(self):
# 获取图幅号列表
with open(self.sheet_designation_path, 'r', encoding='utf-8') as file:
lines = file.readlines()
mesh_ids = []
for line in lines:
if line.strip():
mesh_ids.append(int(line.strip()))
return mesh_ids
@staticmethod
def is_little_mesh(mesh_ids):
# 判断是否是空图幅文件
if len(mesh_ids) < 2:
print("传入的图幅框文件中图幅号数量少于两个!不执行接边逻辑!")
return True
return False
@staticmethod
def get_mesh_box_by_id(mesh_box_layer, mesh_id):
# 根据图幅号获取图幅框的feature
filter = '"' + const.MESH_BOX_MESH_ID + '" = ' + "'" + str(mesh_id) + "'"
mesh_box_layer.SetAttributeFilter(filter)
mesh_box_feature_count = mesh_box_layer.GetFeatureCount()
if mesh_box_feature_count != 1:
print("根据图幅号:" + str(mesh_id) + "获取的图幅框的feature不是一个,请查验!")
return None
mesh_box_feature = mesh_box_layer.GetNextFeature()
mesh_box_layer.SetAttributeFilter(None) # 消除属性过滤
return mesh_box_feature
def connect_adjacent_mesh(self, mesh_ids, mesh_box_layer):
# 获取相邻的图幅号对,此处的图幅框已经已经改变
returned_mesh_info = []
mesh_count = len(mesh_ids)
for i in range(mesh_count - 1):
mesh_id1 = mesh_ids[i]
mesh_box_feature1 = self.get_mesh_box_by_id(mesh_box_layer, mesh_id1)
if mesh_box_feature1 is None:
continue
mesh_box_geom1 = mesh_box_feature1.geometry()
for j in range(i + 1, mesh_count):
mesh_id2 = mesh_ids[j]
mesh_box_feature2 = self.get_mesh_box_by_id(mesh_box_layer, mesh_id2)
if mesh_box_feature2 is None:
continue
mesh_box_geom2 = mesh_box_feature2.geometry()
intersection_geom = mesh_box_geom1.Intersection(mesh_box_geom2)
if not intersection_geom.IsEmpty() and len(intersection_geom.GetPoints()) == 2:
mesh_box_public_border = [[data[0], data[1], 0] for data in intersection_geom.GetPoints()]
solution = ConnectTwoMesh(mesh_id1, mesh_id2, mesh_box_public_border, mesh_box_geom1,
mesh_box_geom2, self.map_data_source)
solution.models()
if solution.mesh_info:
returned_mesh_info += solution.mesh_info
file_str = self.mesh_box_path.split('\\')[:-1]
file_dir = '\\'.join(file_str)
files = os.listdir(file_dir)
file_str.append(const.RETURNED_MESH_INFO_FILE_NAME)
returned_mesh_info_path = '\\'.join(file_str)
if const.RETURNED_MESH_INFO_FILE_NAME in files:
os.remove(returned_mesh_info_path)
# if not returned_mesh_info:
# return
# file_str = self.mesh_box_path.split('\\')[:-1]
# file_str.append(const.RETURNED_MESH_INFO_FILE_NAME)
# returned_mesh_info_path = '\\'.join(file_str)
with open(returned_mesh_info_path, 'w', encoding='utf-8') as file:
file.write("第一列为图幅框1的id\n")
file.write("第二列为图幅框2的id\n")
file.write("第三列为参考线1的id\n")
file.write("第四列为参考线2的id\n\n")
for info in returned_mesh_info:
file.write(str(info[0]) + " " + str(info[1]) + " " + str(info[2]) + " " + str(info[3]))
class ConnectMeshs(BaseConnectMesh):
"""
连接所有的图幅框中的道路
"""
def __init__(self, connect_postgresql, sheet_designation_path, mesh_box_path):
super().__init__(connect_postgresql, sheet_designation_path, mesh_box_path)
def backup_mesh_box_file(self, mesh_ids):
# 备份图幅框文件,并将不在图幅列表中的图幅框删除
# 先删除原始图幅框文件中的空feature
delete_fids = []
mesh_box_layer = self.mesh_box_data_source.GetLayerByName(const.MESH_BOX_LAYER_NAME)
mesh_box_feature_count = mesh_box_layer.GetFeatureCount()
mesh_box_layer.StartTransaction()
for j in range(mesh_box_feature_count):
mesh_box_feature = mesh_box_layer.GetNextFeature()
if not mesh_box_feature:
delete_fids.append(mesh_box_feature.GetFID())
for delete_fid in delete_fids:
mesh_box_layer.DeleteFeature(delete_fid)
mesh_box_layer.CommitTransaction()
mesh_ids = mesh_ids + [] # 相当于复制了一份
file_str = self.mesh_box_path.split('\\')[:-1]
file_str.append(const.BACKUP_MESH_BOX_FILE_NAME)
backup_box_path = '\\'.join(file_str)
shutil.copy(self.mesh_box_path, backup_box_path) # 备份图幅框文件
self.mesh_box_data_source = ogr.Open(backup_box_path, 1) # 重新获取图幅框文件的数据源
mesh_box_layer = self.mesh_box_data_source.GetLayerByName(const.MESH_BOX_LAYER_NAME)
mesh_box_feature_count = mesh_box_layer.GetFeatureCount()
delete_fids = []
mesh_box_layer.StartTransaction()
for i in range(mesh_box_feature_count):
mesh_box_feature = mesh_box_layer.GetNextFeature()
if not mesh_box_feature:
delete_fids.append(mesh_box_feature.GetFID())
continue
mesh_id = mesh_box_feature[const.MESH_BOX_MESH_ID]
if mesh_id in mesh_ids:
mesh_ids.remove(mesh_id) # 不断减小列表,利于加快后面的遍历速度
for delete_fid in delete_fids:
mesh_box_layer.DeleteFeature(delete_fid)
mesh_box_layer.CommitTransaction()
return mesh_box_layer
def models(self):
if not self.is_execute():
return
mesh_ids = self.get_sheet_designations()
result1 = self.is_little_mesh(mesh_ids)
if result1:
return
mesh_box_layer = self.backup_mesh_box_file(mesh_ids)
self.connect_adjacent_mesh(mesh_ids, mesh_box_layer)
self.mesh_box_data_source.Destroy()
self.map_data_source.Destroy()
class ConnectTwoMesh:
"""
此类用于连接两个图幅
"""
def __init__(self, meshid1, meshid2, boundary_line, polygon_geom1, polygon_geom2, dataSource):
self.meshid1 = meshid1
self.meshid2 = meshid2
self.mesh_blh_boundary_line = boundary_line # 包含两个点
self.polygon_geom1 = polygon_geom1
self.polygon_geom2 = polygon_geom2
self.dataSource = dataSource
self.smoothing_count = const.SMOOTHING_MOUNT # 接边处平滑的次数
self.mesh_info = [] # 用于写入未接边情形的图幅号和参考线id信息
self.lane_edge_layer = self.dataSource.GetLayerByName(const.MESH_HD_LANE_EDGE)
self.lane_link_layer = self.dataSource.GetLayerByName(const.MESH_HD_LANE_LINK)
self.link_layer = self.dataSource.GetLayerByName(const.MESH_HD_LINK)
self.lane_edge_node_layer = self.dataSource.GetLayerByName(const.MESH_HD_LANE_EDGE_NODE)
self.lane_node_layer = self.dataSource.GetLayerByName(const.MESH_HD_LANE_NODE)
self.node_layer = self.dataSource.GetLayerByName(const.MESH_HD_NODE)
def dataset_direction1(self, geom):
# 获取数据集的方向,指向边界线为正向,数据没有越界
# intersection为延申的交点
blh_points = geom.GetPoints()
utm_points = transform_to_utm(blh_points, self.source_srs)[0]
utm_points = remove_duplicate_data(utm_points)
intersection = intersection_of_two_line(utm_points[0], utm_points[-1],
self.mesh_utm_boundary_line[0], self.mesh_utm_boundary_line[-1])
distance1 = two_point_distance(utm_points[0][:2], intersection[:2]) # 仅计算二维
distance2 = two_point_distance(utm_points[-1][:2], intersection[:2])
if distance1 < distance2:
flag = 1
utm_points = utm_points[::-1]
else:
flag = 0
return utm_points, flag
def dataset_direction2(self, geom):
# 获取数据集的方向,指向边界线为正向,数据没有越界
# intersection为延申的交点
blh_points = geom.GetPoints()
utm_points = transform_to_utm(blh_points, self.source_srs)[0]
utm_points = remove_duplicate_data(utm_points)
intersection = intersection_of_two_line(utm_points[0], utm_points[-1],
self.mesh_utm_boundary_line[0], self.mesh_utm_boundary_line[-1])
distance1 = two_point_distance(utm_points[0][:2], intersection[:2]) # 仅计算二维
distance2 = two_point_distance(utm_points[-1][:2], intersection[:2])
if abs(distance1 - distance2) < 0.15:
# 此处认为会有0.15的误差
return False
if distance1 < distance2:
flag = 1
utm_points = utm_points[::-1]
else:
flag = 0
return utm_points, flag
def dataset_direction(self, feature, polygon_geom1):
# 获取数据集的方向,指向图幅相交边界线为正向
# feature对应的图幅号与polygon1对应的图幅号相同
geom = feature.GetGeometryRef()
if polygon_geom1.Contains(geom):
utm_points, flag = self.dataset_direction1(geom)
else:
geom1 = geom.Intersection(polygon_geom1) # 取框内的部分
if geom1.IsEmpty():
# 全部在图幅框外
utm_points1, flag1 = self.dataset_direction1(geom)
flag = abs(flag1 - 1)
utm_points = utm_points1[::-1]
else:
result = self.dataset_direction2(geom1)
if not result:
geom2 = geom.Difference(polygon_geom1) # 选择图幅框外面的
utm_points1, flag1 = self.dataset_direction1(geom2)
flag = abs(flag1 - 1)
else:
utm_points1, flag = result
blh_points = geom.GetPoints()
utm_points = transform_to_utm(blh_points, self.source_srs)[0]
utm_points = remove_duplicate_data(utm_points)
if flag == 1:
utm_points = utm_points[::-1]
return utm_points, flag
@staticmethod
def get_boundary_point(point, x_mean, y_mean, theta, parameter):
new_point = [point[0] - x_mean, point[1] - y_mean]
positive_point = positive_transformation_point(theta, new_point)
X_vector = get_coefficient_vector(positive_point, 1)
Y_predict = float(np.squeeze(np.dot(X_vector.T, parameter))) # 去掉矩阵的维度,变为一个浮点数
negative_point = np.zeros((2, 1))
negative_point[0, 0], negative_point[1, 0] = positive_point[0], Y_predict
result_point = negative_transformation_point(theta, negative_point)
target_point = [result_point[0] + x_mean, result_point[1] + y_mean]
return target_point
def get_connect_boundary(self, link_feature1, lane_edge_features1, lane_link_features1,
link_feature2, lane_edge_features2, lane_link_features2):
# 获取两个图幅文件对应接边的两条道路的共有边界
boundary_point = []
link_utm_dataset1, flag = self.dataset_direction(link_feature1, self.polygon_geom1)
link_utm_dataset2, flag = self.dataset_direction(link_feature2, self.polygon_geom2)
link_middle_point = [(link_utm_dataset1[-1][0] + link_utm_dataset2[-1][0]) / 2,
(link_utm_dataset1[-1][1] + link_utm_dataset2[-1][1]) / 2,
(link_utm_dataset1[-1][2] + link_utm_dataset2[-1][2]) / 2, ]
boundary_point.append(link_middle_point)
for lane_edge_feature1 in lane_edge_features1:
target_lane_edge_feature = self.get_connect_feature(lane_edge_feature1, lane_edge_features1,
lane_edge_features2)
if target_lane_edge_feature:
lane_edge_utm_dataset1, flag = self.dataset_direction(lane_edge_feature1, self.polygon_geom1)
lane_edge_utm_dataset2, flag = self.dataset_direction(target_lane_edge_feature, self.polygon_geom2)
lane_edge_middle_point = [(lane_edge_utm_dataset1[-1][0] + lane_edge_utm_dataset2[-1][0]) / 2,
(lane_edge_utm_dataset1[-1][1] + lane_edge_utm_dataset2[-1][1]) / 2,
(lane_edge_utm_dataset1[-1][2] + lane_edge_utm_dataset2[-1][2]) / 2]
boundary_point.append(lane_edge_middle_point)
for lane_link_feature1 in lane_link_features1:
target_lane_link_feature = self.get_connect_feature(lane_link_feature1, lane_link_features1,
lane_link_features2)
if target_lane_link_feature:
lane_link_utm_dataset1, flag = self.dataset_direction(lane_link_feature1, self.polygon_geom1)
lane_link_utm_dataset2, flag = self.dataset_direction(target_lane_link_feature, self.polygon_geom2)
lane_edge_middle_point = [(lane_link_utm_dataset1[-1][0] + lane_link_utm_dataset2[-1][0]) / 2,
(lane_link_utm_dataset1[-1][1] + lane_link_utm_dataset2[-1][1]) / 2,
(lane_link_utm_dataset1[-1][2] + lane_link_utm_dataset2[-1][2]) / 2]
boundary_point.append(lane_edge_middle_point)
# 对获取的交界的点集做直线拟合,获取边界
positive_dataset, theta, x_mean, y_mean = positive_transformation(boundary_point)
parameter = poly_fitting_parameter(positive_dataset, 1)
# 取左边图幅第一条边界西安
target_point1 = self.get_boundary_point(boundary_point[0], x_mean, y_mean, theta, parameter)
target_point1 = [target_point1[0], target_point1[1], boundary_point[0][2]]
target_point2 = self.get_boundary_point(boundary_point[-1], x_mean, y_mean, theta, parameter)
target_point2 = [target_point2[0], target_point2[1], boundary_point[0][2]]
return [target_point1, target_point2]
def get_feature_dataset(self, feature1, feature2):
# 获取feature的方向,指向边界为标准
# 获取feature的数据,并转化为投影数据
utm_points1, flag1 = self.dataset_direction(feature1, self.polygon_geom1)
utm_points2, flag2 = self.dataset_direction(feature2, self.polygon_geom2)
return utm_points1, flag1, utm_points2, flag2
@staticmethod
def single_dis_from_dataset_to_dataset(dataset1, dataset2):
# dataset2到dataset1的距离
positive_dataset, theta, x_mean, y_mean = positive_transformation(dataset1)
parameter = np.squeeze(poly_fitting_parameter(positive_dataset, 1))
distance = 0
for point in dataset2:
new_point = [point[0] - x_mean, point[1] - y_mean]
positive_point = positive_transformation_point(theta, new_point)
distance += dis_from_point_to_line(parameter[1], parameter[0], positive_point)
return distance / len(dataset2)
def dis_from_dataset_to_dataset(self, dataset1, dataset2):
distance1 = self.single_dis_from_dataset_to_dataset(dataset1, dataset2)
distance2 = self.single_dis_from_dataset_to_dataset(dataset2, dataset1)
return (distance1 + distance2) / 2
@staticmethod
def interception_n_meter(dataset, n):
# 从尾部开始,截取长度为n米的点集
point = dataset.pop()
target_dataset = [point]
cumulative_distance = 0
while dataset:
point1 = dataset.pop()
target_dataset.append(point1)
distance = two_point_distance(point, point1)
cumulative_distance += distance
if cumulative_distance > n:
# 仅取n米
break
point = point1
if cumulative_distance <= n:
return target_dataset
distance = two_point_distance(target_dataset[-1], target_dataset[-2])
distance1 = cumulative_distance - distance
interp_dataset = interpolation_between_two_point(target_dataset[-2], target_dataset[-1], 2) # 不保留尾
k = int(len(interp_dataset) * (n - distance1) / distance) + 1
target_dataset = target_dataset[:-2] + interp_dataset[:k]
return target_dataset
def two_feature_distance(self, feature1, feature2):
# 两个feature可能有重合,可能没有
# 每个feature取3个点,做直线拟合,求直线距离
utm_dataset1, flag1 = self.dataset_direction(feature1, self.polygon_geom1)
utm_dataset2, flag2 = self.dataset_direction(feature2, self.polygon_geom2)
dataset1 = self.interception_n_meter(utm_dataset1, 3)
dataset2 = self.interception_n_meter(utm_dataset2, 3)
distance = self.dis_from_dataset_to_dataset(dataset1, dataset2)
return distance
def get_connect_feature(self, feature, features1, features2):
# 从features2中找到与feature连接的feature,feature属于features1
distance_list1 = []
for feature2 in features2:
distance = self.two_feature_distance(feature, feature2)
distance_list1.append(distance)
min_distance1 = min(distance_list1)
if min_distance1 > const.CONNECT_DIS_THRESHOLD:
return False
index1 = distance_list1.index(min_distance1)
target_feature = features2[index1]
distance_list2 = []
for feature1 in features1:
distance = self.two_feature_distance(feature1, target_feature)
distance_list2.append(distance)
min_distance2 = min(distance_list2)
if min_distance2 > const.CONNECT_DIS_THRESHOLD:
return False
index2 = distance_list2.index(min_distance2)
if features1[index2] is feature:
return target_feature
return False
def get_suspicious_link_features(self):
utm__buffer_points = get_buffer_points(self.mesh_utm_boundary_line[0], self.mesh_utm_boundary_line[1])
blh_buffer_points = transform_to_src(utm__buffer_points, self.source_srs, self.utm_srs)
xmin = min([point[0] for point in blh_buffer_points])
xmax = max([point[0] for point in blh_buffer_points])
ymin = min([point[1] for point in blh_buffer_points])
ymax = max([point[1] for point in blh_buffer_points])
self.link_layer.SetSpatialFilterRect(xmin, ymin, xmax, ymax)
link_feature_count = self.link_layer.GetFeatureCount()
if link_feature_count < 2:
print(str(self.meshid1) + "与" + str(self.meshid2) + "的边界相差太远,默认不接边!")
return
link_features1 = [] # 不能使用a = b = [],否则a的变化会反映到b上
link_features2 = []
for i in range(link_feature_count):
link_feature = self.link_layer.GetNextFeature()
if not link_feature:
continue
if link_feature[const.MESH_ID] == self.meshid1:
link_features1.append(link_feature)
elif link_feature[const.MESH_ID] == self.meshid2:
link_features2.append(link_feature)
if not link_features1 or not link_features2:
print(str(self.meshid1) + "或" + str(self.meshid2) + "距离边界太远,默认不接边!")
return
self.link_layer.ResetReading()
new_link_features1 = []
new_link_features2 = []
for link_feature1 in link_features1:
_, flag = self.dataset_direction(link_feature1, self.polygon_geom1)
mark = self.is_last_link_feature(link_feature1, flag)
if mark:
new_link_features1.append(link_feature1)
for link_feature2 in link_features2:
_, flag = self.dataset_direction(link_feature2, self.polygon_geom2)
mark = self.is_last_link_feature(link_feature2, flag)
if mark:
new_link_features2.append(link_feature2)
self.link_layer.SetSpatialFilter(None)
return new_link_features1, new_link_features2
def is_last_link_feature(self, feature, flag):
# 判断是不是一个图幅中的最后一个feature
if flag == 1:
node_id = feature[const.START_NODE_ID]
else:
node_id = feature[const.END_NODE_ID]
meshid = feature[const.MESH_ID]
filter = '"' + const.NODE_ID + '" = ' + str(node_id) + ' and "' + \
const.MESH_ID + '" = ' + "'" + str(meshid) + "'"
# filter = f'"{const.NODE_ID}"={node_id} and "{const.MESH_ID}"= \'{str(meshid)}\''
self.node_layer.SetAttributeFilter(filter)
node_feature_count = self.node_layer.GetFeatureCount()
if node_feature_count != 1:
# 不是一个说明数据有问题
return False
# 此节点关联的参考线只有一条
node_feature = self.node_layer.GetNextFeature()
link_ids = node_feature[const.LINK_IDS]
if ";" in link_ids:
# 说明不止关联一个link
return False
self.node_layer.ResetReading()
self.node_layer.SetAttributeFilter(None)
return True
def is_last_lane_link_feature(self, feature, flag):
# 判断是不是一个图幅中的最后一个feature
if flag == 1:
lane_node_id = feature[const.START_LANE_NODE_ID]
else:
lane_node_id = feature[const.END_LANE_NODE_ID]
meshid = feature[const.MESH_ID]
filter = '"' + const.MESH_LANE_NODE_ID + '" = ' + str(lane_node_id) + 'and "' + \
const.MESH_ID + '" = ' + "'" + str(meshid) + "'"
# filter = f'"{const.MESH_LANE_NODE_ID}"={lane_node_id} and "{const.MESH_ID}"= \'{str(meshid)}\''
self.lane_node_layer.SetAttributeFilter(filter)
lane_node_feature_count = self.lane_node_layer.GetFeatureCount()
if lane_node_feature_count != 1:
# 不是一个说明数据有问题
return False
# 此节点关联的参考线只有一条
lane_node_feature = self.lane_node_layer.GetNextFeature()
lane_link_ids = lane_node_feature[const.LANE_IDS]
if ";" in lane_link_ids:
# 说明不止关联一个link
return False
self.lane_node_layer.ResetReading()
self.lane_node_layer.SetAttributeFilter(None)
return True
def is_last_lane_edge_feature(self, feature, flag):
# 判断是不是一个图幅中的最后一个feature
if flag == 1:
lane_edge_node_id = feature[const.START_EDGE_NODE_ID]
else:
lane_edge_node_id = feature[const.END_EDGE_NODE_ID]
meshid = feature[const.MESH_ID]
filter = '"' + const.MESH_LANE_EDGE_NODE_ID + '" = ' + str(lane_edge_node_id) + ' and "' + \
const.MESH_ID + '" = ' + "'" + str(meshid) + "'"
# filter = f'"{const.MESH_LANE_EDGE_NODE_ID}"={lane_edge_node_id} and ' \
# f'"{const.MESH_ID}"= \'{str(meshid)}\''
self.lane_edge_node_layer.SetAttributeFilter(filter)
lane_edge_node_feature_count = self.lane_edge_node_layer.GetFeatureCount()
if lane_edge_node_feature_count != 1:
# 不是一个说明数据有问题
return False
# 此节点关联的参考线只有一条
lane_edge_node_feature = self.lane_edge_node_layer.GetNextFeature()
lane_edge_ids = lane_edge_node_feature[const.EDGE_IDS]
if ";" in lane_edge_ids:
# 说明不止关联一个link
return False
self.lane_edge_node_layer.ResetReading()
self.lane_edge_node_layer.SetAttributeFilter(None)
return True
@staticmethod
def elevation_judge(dataset1, dataset2, m=10):
# 判断参考线的高程是否在近似同一平面上
# 仅取最后m个点进行操作
dataset1 = dataset1[-m:]
dataset2 = dataset2[-m:]
z1 = [data[2] for data in dataset1]
z2 = [data[2] for data in dataset2]
min_digit = np.inf
for data1 in z1:
for data2 in z2:
digit = abs(data1 - data2)
if digit < min_digit:
min_digit = digit
if min_digit > const.ELEVATION_THRESHOLD:
return False
return True
def connect_judge(self, feature1, feature2, features2):
# 获取前后两个图幅的距离
# feature是参考线的feature
dataset1, flag1, dataset2, flag2 = self.get_feature_dataset(feature1, feature2)
# mark1 = self.is_last_link_feature(feature1, flag1)
# mark2 = self.is_last_link_feature(feature2, flag2)
# if not mark1 or not mark2:
# return False
new_dataset1 = dataset1[-const.LINK_DIS_POINTS:] # 取有限点
# new_dataset2 = dataset2[-const.LINK_DIS_POINTS:]
# distance = self.dis_from_dataset_to_dataset(new_dataset1, new_dataset2)
distance_list = []
for feature in features2:
dataset, flag = self.dataset_direction(feature, self.polygon_geom2)
new_dataset = dataset[-const.LINK_DIS_POINTS:]
distance = self.dis_from_dataset_to_dataset(new_dataset1, new_dataset)
distance_list.append(distance)
min_index = distance_list.index(min(distance_list))
suspicious_feature = features2[min_index]
min_distance = distance_list[min_index]
if suspicious_feature is not feature2 or min_distance > const.LINK_DIS_THRESHOLD:
print(str(self.meshid1) + "与" + str(self.meshid2) + "的参考线相差太远,默认不接边!")
return False
# 对高程的判断,
if not self.elevation_judge(dataset1, dataset2):
print(str(self.meshid1) + "与" + str(self.meshid2) + "的高程值相差太远,默认不接边!")
return False
lane_edge_features1 = self.get_lane_edge_by_link(feature1, self.meshid1)
lane_edge_features2 = self.get_lane_edge_by_link(feature2, self.meshid2)
if not lane_edge_features1 or not lane_edge_features2:
print(str(self.meshid1) + "与" + str(self.meshid2) + "的参考线至少有一条没有关联边界线,默认不接边!")
return False
# 判断这些feature是不是最后一个feature
for lane_edge_feature in lane_edge_features1:
_, flag = self.dataset_direction(lane_edge_feature, self.polygon_geom1)
mark = self.is_last_lane_edge_feature(lane_edge_feature, flag)
if not mark:
return False
for lane_edge_feature in lane_edge_features2:
_, flag = self.dataset_direction(lane_edge_feature, self.polygon_geom2)
mark = self.is_last_lane_edge_feature(lane_edge_feature, flag)
if not mark:
return False
lane_link_features1 = self.get_lane_link_by_link(feature1, self.meshid1)
lane_link_features2 = self.get_lane_link_by_link(feature2, self.meshid2)
if not lane_link_features1 or not lane_link_features2:
print(str(self.meshid1) + "与" + str(self.meshid2) + "的参考线至少有一条没有关联中心线,默认不接边!")
return False
# 判断这些feature是不是最后一个feature
for lane_link_feature in lane_link_features1:
_, flag = self.dataset_direction(lane_link_feature, self.polygon_geom1)
mark = self.is_last_lane_link_feature(lane_link_feature, flag)
if not mark:
return False
for lane_link_feature in lane_link_features2:
_, flag = self.dataset_direction(lane_link_feature, self.polygon_geom2)
mark = self.is_last_lane_link_feature(lane_link_feature, flag)
if not mark:
return False
lane_width1 = 0
m = len(lane_link_features1)
lane_width2 = 0
n = len(lane_link_features2)
if m == 0 or n == 0:
return False
for lane_link_feature in lane_link_features1:
left_lane_edge_id = lane_link_feature[const.LEFT_LANE_EDGE_ID]
right_lane_edge_id = lane_link_feature[const.RIGHT_LANE_EDGE_ID]
left_lane_edge_feature = self.get_lane_edge_by_id(left_lane_edge_id, self.lane_edge_layer)
right_lane_edge_feature = self.get_lane_edge_by_id(right_lane_edge_id, self.lane_edge_layer)
if not left_lane_edge_feature or not right_lane_edge_feature:
continue
left_lane_edge_dataset, _ = self.dataset_direction(left_lane_edge_feature, self.polygon_geom1)
right_lane_edge_dataset, _ = self.dataset_direction(right_lane_edge_feature, self.polygon_geom1)
left_lane_edge_utm_dataset = [[data[0], data[1]] for data in left_lane_edge_dataset] # 仅二维
right_lane_edge_utm_dataset = [[data[0], data[1]] for data in right_lane_edge_dataset] # 仅二维
lane_link_width = dis_from_dataset_to_dataset(left_lane_edge_utm_dataset, right_lane_edge_utm_dataset)
lane_width1 += lane_link_width
for lane_link_feature in lane_link_features2:
left_lane_edge_id = lane_link_feature[const.LEFT_LANE_EDGE_ID]
right_lane_edge_id = lane_link_feature[const.RIGHT_LANE_EDGE_ID]
left_lane_edge_feature = self.get_lane_edge_by_id(left_lane_edge_id, self.lane_edge_layer)
right_lane_edge_feature = self.get_lane_edge_by_id(right_lane_edge_id, self.lane_edge_layer)
if not left_lane_edge_feature or not right_lane_edge_feature:
continue
left_lane_edge_dataset, _ = self.dataset_direction(left_lane_edge_feature, self.polygon_geom2)
right_lane_edge_dataset, _ = self.dataset_direction(right_lane_edge_feature, self.polygon_geom2)
left_lane_edge_utm_dataset = [[data[0], data[1]] for data in left_lane_edge_dataset] # 仅二维
right_lane_edge_utm_dataset = [[data[0], data[1]] for data in right_lane_edge_dataset] # 仅二维
lane_link_width = dis_from_dataset_to_dataset(left_lane_edge_utm_dataset, right_lane_edge_utm_dataset)
lane_width2 += lane_link_width
if (m == n and abs(lane_width1 - lane_width2) > 4000) or \
(abs(m - n) >= 1 and (abs(lane_width1 - lane_width2) < 600)):
self.mesh_info.append([self.meshid1, self.meshid2, feature1[const.LINK_ID], feature2[const.LINK_ID]])
return False
return lane_edge_features1, lane_edge_features2, lane_link_features1, lane_link_features2
@staticmethod
def get_lane_edge_by_id(lane_edge_id, lane_edge_layer):
# 返回关联这个参考线的边线feature
filter = '"' + const.LANE_EDGE_ID + '" = ' + str(lane_edge_id)
lane_edge_layer.SetAttributeFilter(filter)
lane_edge_feature_count = lane_edge_layer.GetFeatureCount()
if lane_edge_feature_count != 1:
return None
else:
lane_edge_feature = lane_edge_layer.GetNextFeature()
lane_edge_layer.ResetReading()
lane_edge_layer.SetAttributeFilter(None)
return lane_edge_feature
def get_node_by_id(self, node_id, meshid):
filter = '"' + const.MESH_NODE_ID + '" = ' + str(node_id) + ' and "' + \
const.MESH_ID + '" = ' + "'" + str(meshid) + "'"
# filter = f'"{const.MESH_NODE_ID}"= {node_id} and "{const.MESH_ID}"= \'{str(meshid)}\''
self.node_layer.SetAttributeFilter(filter)
node_count = self.node_layer.GetFeatureCount()
if node_count != 1:
print("根据参考线节点id:" + str(node_id) + "搜索到的参考线node不是一个,节点状态无法修改")
return
node_feature = self.node_layer.GetNextFeature()
self.node_layer.ResetReading()
self.node_layer.SetAttributeFilter(None)
return node_feature
def get_lane_link_by_link(self, link_feature, meshid):
link_id = link_feature[const.LINK_ID]
filter = '"' + const.HD_LINK_ID + '" = ' + str(link_id) + ' and "' + \
const.MESH_ID + '" = ' + "'" + str(meshid) + "'"
# filter = f'"{const.HD_LINK_ID}"={link_id} and "{const.MESH_ID}"= \'{str(meshid)}\''
self.lane_link_layer.SetAttributeFilter(filter)
lane_link_feature = self.lane_link_layer.GetNextFeature()
lane_link_features = []
while lane_link_feature is not None:
lane_link_features.append(lane_link_feature)
lane_link_feature = self.lane_link_layer.GetNextFeature()
self.lane_link_layer.ResetReading()
self.lane_link_layer.SetAttributeFilter(None)
return lane_link_features
def get_lane_link_node_by_id(self, lane_link_node_id, meshid):
filter = '"' + const.MESH_LANE_NODE_ID + '" = ' + str(lane_link_node_id) + ' and "' + \
const.MESH_ID + '" = ' + "'" + str(meshid) + "'"
# filter = f'"{const.MESH_LANE_NODE_ID}"= {lane_link_node_id} and "{const.MESH_ID}" = \'{str(meshid)}\''
self.lane_node_layer.SetAttributeFilter(filter)
node_count = self.lane_node_layer.GetFeatureCount()
if node_count != 1:
print("根据id:" + str(lane_link_node_id) + "搜索到的中心线node不是一个,节点状态无法修改")
return
link_node_feature = self.lane_node_layer.GetNextFeature()
self.lane_node_layer.ResetReading()
self.lane_node_layer.SetAttributeFilter(None)
return link_node_feature
def get_lane_edge_by_link(self, link_feature, meshid):
# 返回关联这个参考线的边线feature
link_id = link_feature[const.LINK_ID]
filter = '"' + const.HD_LINK_ID + '" = ' + str(link_id) + ' and "' + \
const.MESH_ID + '" = ' + "'" + str(meshid) + "'"
# filter = f'"{const.HD_LINK_ID}"={link_id} and "{const.MESH_ID}"= \'{str(meshid)}\''
self.lane_edge_layer.SetAttributeFilter(filter)
lane_edge_feature = self.lane_edge_layer.GetNextFeature()
lane_edge_features = []
while lane_edge_feature is not None:
lane_edge_features.append(lane_edge_feature)
lane_edge_feature = self.lane_edge_layer.GetNextFeature()
self.lane_edge_layer.ResetReading()
self.lane_edge_layer.SetAttributeFilter(None)
return lane_edge_features
def get_lane_edge_node_by_id(self, lane_edge_node_id, meshid):
filter = '"' + const.MESH_LANE_EDGE_NODE_ID + '" = ' + str(lane_edge_node_id) + \
'and "' + const.MESH_ID + '" = ' + "'" + str(meshid) + "'"
# filter = f'"{const.MESH_LANE_EDGE_NODE_ID}"= {lane_edge_node_id} and ' \
# f'"{const.MESH_ID}" = \'{str(meshid)}\''
self.lane_edge_node_layer.SetAttributeFilter(filter)
node_count = self.lane_edge_node_layer.GetFeatureCount()
if node_count != 1:
print("根据id:" + str(lane_edge_node_id) + "搜索到的边界线node不是一个,节点状态无法修改")
return
link_node_feature = self.lane_edge_node_layer.GetNextFeature()
self.lane_edge_node_layer.ResetReading()
self.lane_edge_node_layer.SetAttributeFilter(None)
return link_node_feature
def get_standard_dataset(self, utm_dataset1, utm_dataset2, flag1, flag2, connect_boundary):
# 获取连接后的标准数据集,参数中的数据都是投影数据
# 仅取有限的数据求得交点,避免点数太多使得数据非直线
new_utm_dataset1 = utm_dataset1[-const.LINK_DIS_POINTS:]
intersection_point1 = intersection_of_two_line(new_utm_dataset1[0], new_utm_dataset1[-1],
connect_boundary[0], connect_boundary[-1])
interpolation_dataset1 = interpolation_dataset(utm_dataset1, const.MESH_INTERPOLATION_MOUNT1)
target_utm_dataset1 = truncate_or_offset_line(interpolation_dataset1, intersection_point1)
target_utm_dataset1 = DouglasPeuker().models(target_utm_dataset1)[::-1]
# target_utm_dataset1 = truncate_or_offset_line(utm_dataset1, intersection_point1)
new_utm_dataset2 = utm_dataset2[-const.LINK_DIS_POINTS:]
intersection_point2 = intersection_of_two_line(new_utm_dataset2[0], new_utm_dataset2[-1],
connect_boundary[0], connect_boundary[-1])
interpolation_dataset2 = interpolation_dataset(utm_dataset2, const.MESH_INTERPOLATION_MOUNT1)
target_utm_dataset2 = truncate_or_offset_line(interpolation_dataset2, intersection_point2)
target_utm_dataset2 = DouglasPeuker().models(target_utm_dataset2)[::-1]
# target_utm_dataset2 = truncate_or_offset_line(utm_dataset2, intersection_point2)
smoothing_dataset1, smoothing_dataset2, middle_intersection_point = \
self.smoothing_dataset(target_utm_dataset1, target_utm_dataset2, connect_boundary)
blh_middle_intersection_point = transform_to_src([middle_intersection_point], self.source_srs, self.utm_srs)[0]
target_blh_dataset1 = transform_to_src(smoothing_dataset1, self.source_srs, self.utm_srs)
target_blh_dataset2 = transform_to_src(smoothing_dataset2, self.source_srs, self.utm_srs)
target_blh_dataset1.append(blh_middle_intersection_point)
target_blh_dataset2.append(blh_middle_intersection_point)
if flag1 == 1:
target_blh_dataset1 = target_blh_dataset1[::-1]
if flag2 == 1:
target_blh_dataset2 = target_blh_dataset2[::-1]
# flag - 1的值代表起点还是终点,0代表起点,-1代表终点,用于修改点图层
return target_blh_dataset1, flag1 - 1, target_blh_dataset2, flag2 - 1, blh_middle_intersection_point
def get_smoothing_count(self, dataset1, dataset2):
# dataset1和dataset2分别是对应接边的数据集
# 仅取最后几个点进行计算
if len(dataset1) == 1 and len(dataset2) == 2:
smoothing_count = 50 # 此种情况下,写死即可
return smoothing_count
elif len(dataset1) == 1 and len(dataset2) > 1:
distance = self.single_dis_from_dataset_to_dataset(dataset2[-3:], dataset1)
elif len(dataset1) > 1 and len(dataset2) == 1:
distance = self.single_dis_from_dataset_to_dataset(dataset1[-3:], dataset2)
else:
distance = self.dis_from_dataset_to_dataset(dataset1[-3:], dataset2[-3:]) # 根据距离计算平滑次数
# 避免太接近导致没有平滑
if distance < 0.5:
smoothing_count = int(self.smoothing_count / 5)
else:
smoothing_count = int(distance * self.smoothing_count)
return smoothing_count
@staticmethod
def remove_near_points(dataset):
# 去除近点
dataset1 = copy.copy(dataset[::-1])
target_dataset = []
point1 = dataset1.pop()
target_dataset.append(point1)
while dataset1:
point2 = dataset1.pop()
distance = two_point_distance(point1, point2)
if distance < const.NEAR_POINT_THRESHOLD:
continue
target_dataset.append(point2)
point1 = point2
return target_dataset
def smoothing_dataset(self, dataset1, dataset2, connect_boundary):
point11 = dataset1.pop()
point21 = dataset2.pop()
new_dataset1 = [point11]
new_dataset2 = [point21]
len1 = 0
len2 = 0
while dataset1:
point12 = dataset1.pop()
new_dataset1.append(point12)
dis1 = two_point_distance(point11, point12)
len1 += dis1
if len1 > const.SMOOTHING_DATA_LENGTH:
break
point11 = point12
new_dataset1 = new_dataset1[::-1]
while dataset2:
point22 = dataset2.pop()
new_dataset2.append(point22)
dis2 = two_point_distance(point21, point22)
len2 += dis2
if len2 > const.SMOOTHING_DATA_LENGTH:
break
new_dataset2 = new_dataset2[::-1]
smoothing_dataset = [] + new_dataset1
smoothing_dataset += new_dataset2[::-1]
smoothing_count = self.get_smoothing_count(new_dataset1, new_dataset2) # 计算平滑次数
# 数据插值、平滑、抽稀、插值、去重
interpolation_smoothing_dataset = interpolation_dataset(smoothing_dataset, const.MESH_INTERPOLATION_MOUNT1)
# target_smoothing_dataset = automatic_smoothing(interpolation_smoothing_dataset, self.smoothing_count)
target_smoothing_dataset = automatic_smoothing(interpolation_smoothing_dataset, smoothing_count)
vacuate_dataset = DouglasPeuker().models(target_smoothing_dataset)[::-1]
interpolation_data = interpolation_dataset(vacuate_dataset, const.MESH_INTERPOLATION_MOUNT2)
# result_dataset = remove_duplicate_data(interpolation_data)
result_dataset = self.remove_near_points(interpolation_data)
# 以边界为界,将平滑数据分类
mark = clockwise_counterclockwise(connect_boundary[0], connect_boundary[-1], result_dataset[0])
for i in range(len(result_dataset)):
mark1 = clockwise_counterclockwise(connect_boundary[0], connect_boundary[-1], result_dataset[i])
if mark1 != mark:
break
smoothing_new_dataset1 = result_dataset[:i]
smoothing_new_dataset2 = result_dataset[i:][::-1]
intersection_point = intersection_of_two_line(smoothing_new_dataset1[-1], smoothing_new_dataset2[-1],
connect_boundary[0], connect_boundary[-1])
target_dataset1 = dataset1 + smoothing_new_dataset1
target_dataset2 = dataset2 + smoothing_new_dataset2
return target_dataset1, target_dataset2, intersection_point
def connect_link(self, link_feature1, link_feature2, connect_boundary):
utm_link_dataset1, flag1, utm_link_dataset2, flag2 = self.get_feature_dataset(link_feature1, link_feature2)
target_blh_dataset1, flag1, target_blh_dataset2, flag2, blh_middle_intersection_point = \
self.get_standard_dataset(utm_link_dataset1, utm_link_dataset2, flag1, flag2, connect_boundary)
geom1 = create_line_geometry(target_blh_dataset1)
geom2 = create_line_geometry(target_blh_dataset2)
self.link_layer.StartTransaction()
link_feature1.SetGeometry(geom1)
link_feature2.SetGeometry(geom2)
self.link_layer.SetFeature(link_feature1)
self.link_layer.SetFeature(link_feature2)
self.link_layer.CommitTransaction()
return flag1, flag2, blh_middle_intersection_point
def connect_node(self, link_feature, flag, blh_target_point, meshid):
# 连接参考线节点
if flag == 0:
node_id = link_feature[const.START_NODE_ID]
else:
node_id = link_feature[const.END_NODE_ID]
node_feature = self.get_node_by_id(node_id, meshid)
if not node_feature:
return
geom = create_point_geometry(blh_target_point)
self.node_layer.StartTransaction()
node_feature.SetGeometry(geom)
self.node_layer.SetFeature(node_feature)
self.node_layer.CommitTransaction()
# link_feature不能释放,因为在for中,target_link_feature1必须始终保持着
node_feature.Destroy()
def connect_lane_edge(self, lane_edge_feature1, lane_edge_feature2, connect_boundary):
utm_lane_edge_dataset1, flag1, utm_lane_edge_dataset2, flag2 = \
self.get_feature_dataset(lane_edge_feature1, lane_edge_feature2)
target_blh_dataset1, flag1, target_blh_dataset2, flag2, blh_middle_intersection_point = \
self.get_standard_dataset(utm_lane_edge_dataset1, utm_lane_edge_dataset2, flag1, flag2, connect_boundary)
geom1 = create_line_geometry(target_blh_dataset1)
geom2 = create_line_geometry(target_blh_dataset2)
self.lane_edge_layer.StartTransaction()
lane_edge_feature1.SetGeometry(geom1)
lane_edge_feature2.SetGeometry(geom2)
self.lane_edge_layer.SetFeature(lane_edge_feature1)
self.lane_edge_layer.SetFeature(lane_edge_feature2)
self.lane_edge_layer.CommitTransaction()
return flag1, flag2, blh_middle_intersection_point
def connect_lane_edge_node(self, lane_edge_feature, flag, blh_target_point, meshid):
if flag == 0:
lane_edge_node_id = lane_edge_feature[const.START_EDGE_NODE_ID]
else:
lane_edge_node_id = lane_edge_feature[const.END_EDGE_NODE_ID]
lane_edge_node_feature = self.get_lane_edge_node_by_id(lane_edge_node_id, meshid)
if not lane_edge_node_feature:
return
geom = create_point_geometry(blh_target_point)
self.lane_edge_node_layer.StartTransaction()
lane_edge_node_feature.SetGeometry(geom)
self.lane_edge_node_layer.SetFeature(lane_edge_node_feature)
self.lane_edge_node_layer.CommitTransaction()
lane_edge_node_feature.Destroy()
def connect_lane_link(self, lane_link_feature1, lane_link_feature2, connect_boundary):
utm_lane_link_dataset1, flag1, utm_lane_link_dataset2, flag2 = \
self.get_feature_dataset(lane_link_feature1, lane_link_feature2)
target_blh_dataset1, flag1, target_blh_dataset2, flag2, blh_middle_intersection_point = \
self.get_standard_dataset(utm_lane_link_dataset1, utm_lane_link_dataset2, flag1, flag2, connect_boundary)
geom1 = create_line_geometry(target_blh_dataset1)
geom2 = create_line_geometry(target_blh_dataset2)
self.lane_link_layer.StartTransaction()
lane_link_feature1.SetGeometry(geom1)
lane_link_feature2.SetGeometry(geom2)
self.lane_link_layer.SetFeature(lane_link_feature1)
self.lane_link_layer.SetFeature(lane_link_feature2)
self.lane_link_layer.CommitTransaction()
return flag1, flag2, blh_middle_intersection_point
def connect_lane_link_node(self, lane_link_feature, flag, blh_target_point, meshid):
if flag == 0:
lane_link_node_id = lane_link_feature[const.START_LANE_NODE_ID]
else:
lane_link_node_id = lane_link_feature[const.END_LANE_NODE_ID]
lane_node_feature = self.get_lane_link_node_by_id(lane_link_node_id, meshid)
if not lane_node_feature:
return
geom = create_point_geometry(blh_target_point)
self.lane_node_layer.StartTransaction()
lane_node_feature.SetGeometry(geom)
self.lane_node_layer.SetFeature(lane_node_feature)
self.lane_node_layer.CommitTransaction()
lane_node_feature.Destroy()
def models(self):
if not self.lane_edge_layer or not self.lane_link_layer or not self.link_layer or \
not self.lane_edge_node_layer or not self.lane_node_layer or not self.node_layer:
print("线图层和节点图层没有全部获取到,请查验数据是否正确!")
return
self.source_srs = self.lane_link_layer.GetSpatialRef() # 选择中心线的图层坐标,所有图层的坐标系统一致
self.mesh_utm_boundary_line, self.utm_srs = transform_to_utm(self.mesh_blh_boundary_line, self.source_srs)
result = self.get_suspicious_link_features()
if not result:
return
link_features1, link_features2 = result
m = len(link_features1)
n = len(link_features2)
for i in range(m):
target_link_feature1 = link_features1[i]
id1 = target_link_feature1["LINK_ID"]
for j in range(n):
target_link_feature2 = link_features2[j]
id2 = target_link_feature2["LINK_ID"]
connect_judge_ = self.connect_judge(target_link_feature1, target_link_feature2, link_features2)
if not connect_judge_:
continue
lane_edge_features1, lane_edge_features2, lane_link_features1, lane_link_features2 = connect_judge_
# 连接参考线和节点
connect_boundary = self.get_connect_boundary(target_link_feature1, lane_edge_features1,
lane_link_features1, target_link_feature2,
lane_edge_features2, lane_link_features2)
flag1, flag2, blh_link_middle_intersection_point = self.connect_link(target_link_feature1,
target_link_feature2,
connect_boundary)
self.connect_node(target_link_feature1, flag1, blh_link_middle_intersection_point, self.meshid1)
self.connect_node(target_link_feature2, flag2, blh_link_middle_intersection_point, self.meshid2)
# 连接边界线和节点
for lane_edge_feature1 in lane_edge_features1:
lane_edge_feature2 = self.get_connect_feature(lane_edge_feature1, lane_edge_features1,
lane_edge_features2)
if not lane_edge_feature2:
continue
flag1, flag2, blh_middle_intersection_point = self.connect_lane_edge(lane_edge_feature1,
lane_edge_feature2,
connect_boundary)
self.connect_lane_edge_node(lane_edge_feature1, flag1, blh_middle_intersection_point,
self.meshid1)
self.connect_lane_edge_node(lane_edge_feature2, flag2, blh_middle_intersection_point,
self.meshid2)
# 连接中心线和节点
for lane_link_feature1 in lane_link_features1:
lane_link_feature2 = self.get_connect_feature(lane_link_feature1, lane_link_features1,
lane_link_features2)
if not lane_link_feature2:
continue
flag1, flag2, blh_middle_intersection_point = self.connect_lane_link(lane_link_feature1,
lane_link_feature2,
connect_boundary)
self.connect_lane_link_node(lane_link_feature1, flag1, blh_middle_intersection_point,
self.meshid1)
self.connect_lane_link_node(lane_link_feature2, flag2, blh_middle_intersection_point,
self.meshid2)
for lane_edge_feature1 in lane_edge_features1:
lane_edge_feature1.Destroy()
for lane_edge_feature2 in lane_edge_features2:
lane_edge_feature2.Destroy()
for lane_link_feature1 in lane_link_features1:
lane_link_feature1.Destroy()
for lane_link_feature2 in lane_link_features2:
lane_link_feature2.Destroy()
target_link_feature1.Destroy()
for link_feature in link_features2:
link_feature.Destroy()
# _*_ coding: utf-8 _*_ #
# @Time: 2020/1/21 13:18
# @Author: XiongChao
# @Time: 2020/5/18 15:37
# @Author: XiongChao
\ No newline at end of file
# _*_ coding: utf-8 _*_ #
# @Time: 2020/4/22 17:04
# @Author: XiongChao
import csv
from connect_solution.coordinate_transformation import *
from util.common import *
from math_tools.data_interpolation import interpolation_dataset_3d
from connect_solution.geographic_tool import *
from math_tools.polynomial_fitting import *
from math_tools.savitsky_golay_filter import *
from math_tools.douglas_peucker_2d import *
from util import const
class ConnectMeshByMeshList:
"""
根据图幅号列表进行接边,需要传入图幅号列表和连接数据库
"""
def __init__(self, hd_data_source, mesh_box_layer, mesh_ids, output_document_path):
self.hd_data_source = hd_data_source
self.mesh_box_layer = mesh_box_layer
self.mesh_ids = mesh_ids
self.output_document_path = output_document_path
self.output_info1 = [] # 用于存储未接边输出的信息
self.output_info2 = [] # 用于存储已接边输出的信息
self.output_info3 = [] # 用于存储不需要接边的信息
self.topo_point_layer = self.hd_data_source.GetLayerByName(const.HD_TOPO_P)
self.link_layer = self.hd_data_source.GetLayerByName(const.HD_LINK)
self.node_layer = self.hd_data_source.GetLayerByName(const.HD_NODE)
self.lane_link_layer = self.hd_data_source.GetLayerByName(const.HD_LANE_LINK)
self.lane_node_layer = self.hd_data_source.GetLayerByName(const.HD_LANE_NODE)
self.lane_edge_layer = self.hd_data_source.GetLayerByName(const.HD_LANE_EDGE)
self.lane_edge_node_layer = self.hd_data_source.GetLayerByName(const.HD_LANE_EDGE_NODE)
self.link_edge_layer = self.hd_data_source.GetLayerByName(const.HD_LINK_EDGE)
self.link_edge_node_layer = self.hd_data_source.GetLayerByName(const.HD_LINK_EDGE_NODE)
def get_box_by_meshid(self, mesh_id):
# 根据图幅号获取图幅框
filter = '"' + const.MESH_ID + '" = ' + "'" + str(mesh_id) + "'"
self.mesh_box_layer.SetAttributeFilter(filter)
mesh_box_count = self.mesh_box_layer.GetFeatureCount()
if mesh_box_count != 1:
return False
mesh_box_feature = self.mesh_box_layer.GetNextFeature()
self.mesh_box_layer.SetAttributeFilter(None) # 消除属性过滤
self.mesh_box_layer.ResetReading()
return mesh_box_feature
def get_waiting_connect_meshs(self, mesh_box_feature, mesh_id):
mesh_box_geom = mesh_box_feature.GetGeometryRef()
mesh_box_buffer = mesh_box_geom.Buffer(const.BUFFER_SIZE1, 4)
mesh_box_buffer_points = mesh_box_buffer.GetGeometryRef(0).GetPoints()
lon_list = [point[0] for point in mesh_box_buffer_points]
lat_list = [point[1] for point in mesh_box_buffer_points]
min_lon, max_lon = min(lon_list), max(lon_list)
min_lat, max_lat = min(lat_list), max(lat_list)
self.mesh_box_layer.SetSpatialFilterRect(min_lon, min_lat, max_lon, max_lat)
mesh_box_count = self.mesh_box_layer.GetFeatureCount()
mesh_box_features = []
for _ in range(mesh_box_count):
mesh_box_feature = self.mesh_box_layer.GetNextFeature()
if mesh_box_feature is None or mesh_box_feature[const.MESH_ID] == mesh_id:
continue
mesh_box_features.append(mesh_box_feature)
self.mesh_box_layer.SetSpatialFilter(None) # 消除空间过滤
self.mesh_box_layer.ResetReading()
return mesh_box_features
@staticmethod
def construct_opposite_buffer(intersect_point):
# 根据顶点的交点构造一个buffer
lon, lat = intersect_point[0], intersect_point[1]
min_lon, max_lon = lon - const.BUFFER_SIZE4, lon + const.BUFFER_SIZE4
min_lat, max_lat = lat - const.BUFFER_SIZE4, lat + const.BUFFER_SIZE4
return min_lon, max_lon, min_lat, max_lat
@staticmethod
def construct_adjacent_buffer(intersect_points):
# 根据共有边线构造buffer框
point1, point2 = intersect_points[0], intersect_points[1]
if abs(point1[0] - point2[0]) < 0.000000001:
# 经度相同,边线垂直向下
min_lon, max_lon = point1[0] - const.BUFFER_SIZE2, point1[0] + const.BUFFER_SIZE2
min_lat, max_lat = min(point1[1], point2[1]), max(point1[1], point2[1])
else:
# 纬度相同,边线水平
min_lon, max_lon = min(point1[0], point2[0]), max(point1[0], point2[0])
min_lat, max_lat = point1[1] - const.BUFFER_SIZE2, point2[1] + const.BUFFER_SIZE2
return min_lon, max_lon, min_lat, max_lat
def is_effective_data(self):
if self.topo_point_layer is None:
output_info = "输入的数据没有拓扑点图层,请查验数据后接边"
self.output_info1.append(['', '', output_info])
return False
if self.link_layer is None:
output_info = "输入的数据没有参考线图层,请查验数据后接边"
self.output_info1.append(['', '', output_info])
return False
if self.node_layer is None:
output_info = "输入的数据没有参考线节点图层,请查验数据后接边"
self.output_info1.append(['', '', output_info])
return False
if self.lane_link_layer is None:
output_info = "输入的数据没有中心线图层,请查验数据后接边"
self.output_info1.append(['', '', output_info])
return False
if self.lane_node_layer is None:
output_info = "输入的数据没有中心线节点图层,请查验数据后接边"
self.output_info1.append(['', '', output_info])
return False
if self.lane_edge_layer is None:
output_info = "输入的数据没有边界线图层,请查验数据后接边"
self.output_info1.append(['', '', output_info])
return False
if self.lane_edge_node_layer is None:
output_info = "输入的数据没有边界线节点图层,请查验数据后接边"
self.output_info1.append(['', '', output_info])
return False
return True
def write_info(self):
# 将存储的接边信息写到指定的文件路径下,此处模式为添加
path1 = self.output_document_path + "\\未接边信息.csv"
path2 = self.output_document_path + "\\已接边信息.txt"
path3 = self.output_document_path + "\\不需要接边信息.txt"
with open(path1, 'a', encoding='utf_8_sig', newline='') as file1:
writer1 = csv.writer(file1)
writer1.writerow([" "])
writer1.writerow(["序号", "图幅号1", "图幅号2", "接边报错信息"])
index = 1
for info in self.output_info1:
error_info = [str(index), info[0], info[1]]
for error_message in info[2]:
error_info.append(str(error_message))
writer1.writerow(error_info)
index += 1
with open(path2, 'a', encoding='utf-8') as file2:
file2.write('\n')
for info in self.output_info2:
file2.write(info + '\n')
with open(path3, 'a', encoding='utf-8') as file3:
file3.write('\n')
for info in self.output_info3:
file3.write(info + '\n')
def is_need_connect(self, min_lon, min_lat, max_lon, max_lat, mesh_id1, mesh_id2):
# 用于输出需要接边的topo点,并且这些topo点有后继信息,可以根据next往后面走
self.topo_point_layer.SetSpatialFilterRect(min_lon, min_lat, max_lon, max_lat)
topo_feature_count = self.topo_point_layer.GetFeatureCount()
if topo_feature_count == 0:
output_info = "图幅号为:" + str(mesh_id1) + "的topo点" + "和图幅号为:" + \
str(mesh_id2) + "的topo点没有连接的关系,不需要接边\n"
self.output_info3.append(output_info)
return False, []
filter = '"' + const.NEXT_COUNT + '" = 0 and "' + const.PREV_COUNT + '" != 0'
self.topo_point_layer.SetAttributeFilter(filter)
topo_feature_count = self.topo_point_layer.GetFeatureCount()
if topo_feature_count == 0:
output_info = "图幅号:" + str(mesh_id1) + "和图幅号:" + str(mesh_id2) + \
"之间没有topo的next_count为0且prev_count不为0,不需要接边\n"
self.output_info3.append(output_info)
return False, []
topo_features = []
for _ in range(topo_feature_count):
topo_feature = self.topo_point_layer.GetNextFeature()
if not topo_feature or not topo_feature[const.NEXT]:
continue
topo_features.append(topo_feature)
self.topo_point_layer.SetAttributeFilter(None) # 消除属性过滤
self.topo_point_layer.SetSpatialFilter(None) # 消除空间过滤
self.topo_point_layer.ResetReading()
if not topo_features:
output_info = "图幅号:" + str(mesh_id1) + "和图幅号:" + str(mesh_id2) + \
"之间没有topo的next_count为0且prev_count不为0且next字段不为空,不需要接边\n"
self.output_info3.append(output_info)
return False, []
target_topo_features = []
gps_ids = []
for topo_feature in topo_features:
if not topo_feature or (not int(topo_feature[const.NEXT][1]) == 1):
continue
_, next_gps_id = topo_feature[const.NEXT][1:-1].split(":")
filter = '"' + const.ID + '" = ' + str(next_gps_id)
self.topo_point_layer.SetAttributeFilter(filter)
topo_count = self.topo_point_layer.GetFeatureCount()
if topo_count == 0:
output_info = "图幅号:" + str(mesh_id1) + "和图幅号:" + str(mesh_id2) + "的接边中,topo点的id为:" + \
str(topo_feature[const.ID]) + ",根据它的next字段没有找到它的准确后继," \
"可能为topo跳点的地方,不接边\n"
self.output_info3.append(output_info)
self.topo_point_layer.SetAttributeFilter(None)
continue
next_topo_feature = None
flag = False
for _ in range(topo_count):
# 如果有多个topo数据,选择有next字段的并且仅有一个next的topo点
next_topo_feature = self.topo_point_layer.GetNextFeature()
if not next_topo_feature:
continue
if int(next_topo_feature[const.NEXT_COUNT]) == 1:
flag = True
break
self.topo_point_layer.SetAttributeFilter(None)
if not flag:
output_info = "图幅号:" + str(mesh_id1) + "和图幅号:" + str(mesh_id2) + "的接边中,topo点的id为:" + \
str(topo_feature[const.ID]) + ",根据它的next字段没有找到它的后继中也有后继的topo点," \
"从topo点判断不是接边处,不接边\n"
self.output_info3.append(output_info)
continue
if topo_feature["MESHID"] != next_topo_feature["MESHID"] and next_topo_feature["MESHID"] in [mesh_id1, mesh_id2]:
# topo点实现跨越图幅
if int(next_topo_feature[const.ID]) not in gps_ids:
gps_ids.append(int(next_topo_feature[const.ID]))
target_topo_features.append(next_topo_feature)
if not target_topo_features:
return False, []
return True, target_topo_features
def reset_link_edge_layer(self):
# 判断是否重置道路边缘图层,如果参考线没有左右道路边缘图层的字段,就重置为None
if self.link_layer.FindFieldIndex(const.LEFT_LINK_EDGE, False) == -1 or \
self.link_layer.FindFieldIndex(const.RIGHT_LINK_EDGE, False) == -1 or \
self.link_edge_layer is None or self.link_edge_layer.GetFeatureCount() == 0:
self.link_edge_layer = None
def is_connect_by_opposite(self, intersect_points, mesh_id1, mesh_id2):
"""
判断对角的情形下是否需要接边
:param intersect_points: 对角图幅的交点
:param mesh_id1: 第一个图幅框的图幅号
:param mesh_id2: 第二个图幅框的图幅号
:return: boolean
"""
min_lon, max_lon, min_lat, max_lat = self.construct_opposite_buffer(intersect_points[0])
self.topo_point_layer.SetSpatialFilterRect(min_lon, min_lat, max_lon, max_lat)
topo_feature_count = self.topo_point_layer.GetFeatureCount()
self.topo_point_layer.SetSpatialFilter(None) # 消除空间过滤器
if topo_feature_count == 0:
output_info = "图幅号为:" + str(mesh_id1) + "的topo点" + "和图幅号为:" + \
str(mesh_id2) + "的topo点没有连接的关系,不需要接边\n"
self.output_info3.append(output_info)
return False
return True
def main(self):
if not self.is_effective_data():
self.write_info()
return
self.reset_link_edge_layer()
connect_info = [self.hd_data_source, self.topo_point_layer, self.link_layer, self.node_layer,
self.lane_link_layer, self.lane_node_layer, self.lane_edge_layer,
self.lane_edge_node_layer, self.link_edge_layer, self.link_edge_node_layer]
two_mesh_solution = ConnectTwoMeshByTopo(*connect_info)
connected_meshs = {} # 存储已经结果边的图幅,格式mesh_id:mesh_id1, mesh_id2, ...
for mesh_id1 in self.mesh_ids:
try:
mesh_box_feature1 = self.get_box_by_meshid(mesh_id1)
connected_meshs[mesh_id1] = []
if mesh_box_feature1 is False:
output_info = "根据图幅号:" + str(mesh_id1) + \
"没有找到图幅框或者找到的图幅框不止一个,数据异常,请修改数据后执行接边"
self.output_info1.append([str(mesh_id1), '', [output_info]])
continue
mesh_box_geom1 = mesh_box_feature1.GetGeometryRef()
mesh_box_features = self.get_waiting_connect_meshs(mesh_box_feature1, mesh_id1)
except Exception as e:
output_info = "在搜索图幅号:" + str(mesh_id1) + "周围应该与之接边的图幅框时,出现异常:" + \
str(e) + ",请手动将需要与之接边的地方接起来"
self.output_info1.append([str(mesh_id1), '', [output_info]])
continue
for mesh_box_feature2 in mesh_box_features:
mesh_box_geom2 = mesh_box_feature2.GetGeometryRef()
mesh_id2 = mesh_box_feature2[const.MESH_ID]
if mesh_id2 in connected_meshs.keys() and mesh_id1 in connected_meshs[mesh_id2]:
# 此时已经接过边了
continue
if mesh_id2 not in connected_meshs[mesh_id1]:
connected_meshs[mesh_id1].append(mesh_id2)
if not mesh_box_geom1.Intersects(mesh_box_geom2):
output_info = "图幅号为:" + str(mesh_id1) + "的图幅框" + "和图幅号为:" + \
str(mesh_id2) + "的图幅框没有相交的部分,不接边\n"
self.output_info3.append(output_info)
continue
intersect_geom = mesh_box_geom1.Intersection(mesh_box_geom2)
intersect_points = intersect_geom.GetPoints()
if len(intersect_points) == 1:
# 表示两个图幅互为对角,如果需要接边,则报出来手动接边,此情形极少
if self.is_connect_by_opposite(intersect_points, mesh_id1, mesh_id2):
output_info = "图幅号为:" + str(mesh_id1) + "的图幅框" + "和图幅号为:" + str(mesh_id2) + \
"的图幅框互为对角,可能要接边,请查验此处是否需要接边,如果需要,请手动接边"
self.output_info1.append([str(mesh_id1), str(mesh_id2), [output_info]])
continue
else:
# 表示两个图幅相邻
min_lon, max_lon, min_lat, max_lat = self.construct_adjacent_buffer(intersect_points)
try:
flag, topo_features = self.is_need_connect(min_lon, min_lat, max_lon, max_lat, mesh_id1, mesh_id2)
except Exception as e:
output_info = "查找图幅号为:" + str(mesh_id1) + " " + str(mesh_id2) + \
"的交界处查找是否有需要接边的topo时,出现异常:" + str(e) + \
"请找到此处判别是否需要接边,如果需要,请手动接边"
self.output_info1.append([str(mesh_id1), str(mesh_id2), [output_info]])
continue
if flag:
# 对每一对topo,从gps上来说这个地方是要连接的
try:
self.hd_data_source.StartTransaction()
two_mesh_solution.models(topo_features, mesh_id1, mesh_id2)
self.hd_data_source.CommitTransaction()
except Exception as e:
output_info = "图幅号为:" + str(mesh_id1) + " " + str(mesh_id2) + \
"的接边出现异常,异常:" + str(e) + ",请手动接边"
self.output_info1.append([str(mesh_id1), str(mesh_id2), [output_info]])
self.hd_data_source.CommitTransaction()
continue
if two_mesh_solution.output_info1:
self.output_info1.append([str(mesh_id1), str(mesh_id2), two_mesh_solution.output_info1])
if two_mesh_solution.output_info2:
output_info = "图幅号为:" + str(mesh_id1) + " " + str(mesh_id2) + "的接边运行开始"
self.output_info2.append(output_info)
self.output_info2 += two_mesh_solution.output_info2
output_info = "图幅号为:" + str(mesh_id1) + " " + str(mesh_id2) + "的接边运行结束\n"
self.output_info2.append(output_info)
two_mesh_solution.output_info1 = []
two_mesh_solution.output_info2 = []
output_info = "\n接边程序程序运行结束\n"
self.output_info2.append(output_info)
self.output_info3.append(output_info)
self.write_info() # 写入接边信息
class ConnectTwoMeshByTopo:
"""
先判断是否需要连接两个图幅,如果需要的话将两个图幅中需要连接的道路连接起来
"""
def __init__(self, hd_data_source, topo_point_layer, link_layer, node_layer, lane_link_layer, lane_node_layer,
lane_edge_layer, lane_edge_node_layer, link_edge_layer=None, link_edge_node_layer=None):
self.hd_data_source = hd_data_source
self.topo_point_layer = topo_point_layer
self.link_layer = link_layer
self.node_layer = node_layer
self.lane_link_layer = lane_link_layer
self.lane_node_layer = lane_node_layer
self.lane_edge_layer = lane_edge_layer
self.lane_edge_node_layer = lane_edge_node_layer
self.link_edge_layer = link_edge_layer
self.link_edge_node_layer = link_edge_node_layer
self.output_info1 = []
self.output_info2 = []
def is_gap_or_big_curve(self, topo_feature, source_srs):
# 判断从某一点开始,后面的gps点有没有间断或者大弯道的情形
move_count = 0
blh_point1 = topo_feature.GetGeometryRef().GetPoints() # 为一元列表
azimuth1 = topo_feature[const.AZIMUTH]
utm_point1 = transform_to_utm(blh_point1, source_srs)[0][0]
while move_count <= const.MOVE_COUNT_THRESHOLD:
next_count = topo_feature[const.NEXT_COUNT]
if not next_count or int(next_count) != 1:
# 在寻找参考线的过程中可能会报错,在那里报出来即可
return False
_, next_topo_id = topo_feature[const.NEXT][1:-1].split(':')
filter = '"' + const.ID + '" = ' + str(next_topo_id)
self.topo_point_layer.SetAttributeFilter(filter)
topo_count = self.topo_point_layer.GetFeatureCount()
if topo_count != 1:
# 在寻找参考线的过程中可能会报错,在那里报出来即可
# 可能到达了屠夫边界
self.topo_point_layer.SetAttributeFilter(None) # 消除属性过滤
return False
topo_feature = self.topo_point_layer.GetNextFeature()
self.topo_point_layer.SetAttributeFilter(None) # 消除属性过滤
self.topo_point_layer.ResetReading()
azimuth2 = topo_feature[const.AZIMUTH]
if abs(abs(abs(azimuth2 - azimuth1) - 180) - 180) > const.AZIMUTH_THRESHOLD:
output_info = "gps_id为: " + str(topo_feature[const.ID]) + "的topo点的接边处为大弯道,请手动接边"
self.output_info1.append(output_info)
return True
blh_point2 = topo_feature.GetGeometryRef().GetPoints()
utm_point2 = transform_to_utm(blh_point2, source_srs)[0][0]
distance = two_point_distance_2d(utm_point1, utm_point2)
if distance > const.DIS_THRESHOLD2:
output_info = "gps_id为: " + str(topo_feature[const.ID]) + "的topo点的接边处为大间断,请手动接边"
self.output_info1.append(output_info)
return True
move_count += 1
utm_point1 = utm_point2
azimuth1 = azimuth2
return False
@staticmethod
def create_buffer(topo_feature):
topo_blh_point = topo_feature.GetGeometryRef().GetPoints()[0]
lon, lat = topo_blh_point[0], topo_blh_point[1]
min_lon, max_lon = lon - const.BUFFER_SIZE3, lon + const.BUFFER_SIZE3
min_lat, max_lat = lat - const.BUFFER_SIZE3, lat + const.BUFFER_SIZE3
return min_lon, max_lon, min_lat, max_lat
def dis_from_topo_to_line(self, topo_feature, line_feature, source_srs):
# 计算一个gps点到一条线的距离
topo_blh_point = topo_feature.GetGeometryRef().GetPoints()
lon, lat, up = topo_blh_point[0][0], topo_blh_point[0][1], topo_blh_point[0][2]
azimuth, roll, pitch = topo_feature[const.AZIMUTH], topo_feature[const.ROLL], topo_feature[const.PITCH]
line_blh_points = line_feature.GetGeometryRef().GetPoints()
line_cpt_points = blh_to_cpt(line_blh_points, lon, lat, up, pitch, roll, azimuth)
# 去除很近点,防止后面报错
new_line_cpt_points = self.remove_near_points(interpolation_dataset_3d(line_cpt_points, const.INTERPOLATION_COUNT1))
point_index1, point_index2 = nearest_m_point_of_dataset_2d([0, 0, 0], new_line_cpt_points, 2)
cpt_points = [new_line_cpt_points[point_index1], new_line_cpt_points[point_index2]]
vector = [cpt_points[1][0] - cpt_points[0][0], cpt_points[1][1] - cpt_points[0][1]]
angle = angle_of_two_vector(vector, [0, 1])
if abs(angle - 90) < const.ANGLE2:
# 与行驶方向差异过大,不计入计算
return np.inf
utm_topo_point = transform_to_utm(topo_blh_point, source_srs)[0][0]
utm_line_points = transform_to_utm(line_blh_points, source_srs)[0]
new_line_utm_points = interpolation_dataset_3d(utm_line_points, const.INTERPOLATION_COUNT1)
point_index = nearest_m_point_of_dataset_2d(utm_topo_point, new_line_utm_points, 1)[0]
return two_point_distance_2d(utm_topo_point, new_line_utm_points[point_index])
def get_lane_edge_intersect_buffer(self, topo_feature, mesh_id1, mesh_id2, source_srs):
min_lon, max_lon, min_lat, max_lat = self.create_buffer(topo_feature)
self.lane_edge_layer.SetSpatialFilterRect(min_lon, min_lat, max_lon, max_lat)
lane_edge_feature_count = self.lane_edge_layer.GetFeatureCount()
if lane_edge_feature_count < 2:
output_info = "gps_id: " + str(topo_feature[const.ID]) + "的topo点附近的车道宽度过大,请手动接边"
self.output_info1.append(output_info)
return []
lane_edge_features = []
for _ in range(lane_edge_feature_count):
lane_edge_feature = self.lane_edge_layer.GetNextFeature()
if lane_edge_feature is None:
continue
distance = self.dis_from_topo_to_line(topo_feature, lane_edge_feature, source_srs)
if distance == np.inf:
# 边界线的朝向与topo的朝向不同
continue
lane_edge_features.append([distance, lane_edge_feature])
self.lane_edge_layer.SetSpatialFilter(None) # 消除空间过滤
self.lane_edge_layer.ResetReading()
if len(lane_edge_features) == 0:
output_info = "根据gps_id: " + str(topo_feature[const.ID]) + \
"构造的buffer没有框选到的非空边界线,或者框选的边界线的方向与topo点的方向差异过大,请手动接边"
self.output_info1.append(output_info)
return []
lane_edge_features = [lane_edge_feature for lane_edge_feature in lane_edge_features
if (int(lane_edge_feature[1][const.MESH_ID]) == mesh_id1 or
int(lane_edge_feature[1][const.MESH_ID]) == mesh_id2) and
lane_edge_feature[0] < const.DIS_THRESHOLD1]
if not lane_edge_features:
output_info = "根据gps_id: " + str(topo_feature[const.ID]) + "构造的buffer框选的边界线与gps点相距太远,请手动接边"
self.output_info1.append(output_info)
# # 获取的边界线距离topo距离较近,判断这些参考线是否关联着相同的参考线
# link_id1 = lane_edge_features[0][1][const.HD_LINK_ID]
# mesh_id1 = lane_edge_features[0][1][const.MESH_ID]
# for distance, lane_edge_feature in lane_edge_features:
# link_id2 = lane_edge_feature[const.HD_LINK_ID]
# if link_id2 != link_id1:
# # 计算两条参考线接近部分的夹角,较大,说明为交叉路
# mesh_id2 = lane_edge_feature[const.MESH_ID]
# output_info = "gps_id: " + str(topo_feature[const.ID]) + "附近可能有交叉路或距离很近的双向道路,请手动接边"
# self.output_info1.append(output_info)
# return []
return lane_edge_features
@staticmethod
def cal_elevation_bias(utm_topo_point, utm_link_points1, utm_link_points2):
# 计算两条参考线的高程偏差;取10个点进行分析
distance1 = two_point_distance_2d(utm_link_points1[0], utm_topo_point)
distance2 = two_point_distance_2d(utm_link_points1[-1], utm_topo_point)
if distance1 <= distance2:
new_utm_link_points1 = utm_link_points1[:10]
else:
new_utm_link_points1 = utm_link_points1[-10:]
distance3 = two_point_distance_2d(utm_link_points2[0], utm_topo_point)
distance4 = two_point_distance_2d(utm_link_points2[-1], utm_topo_point)
if distance3 < distance4:
new_utm_link_points2 = utm_link_points2[:10]
else:
new_utm_link_points2 = utm_link_points2[-10:]
elevation_mean1 = np.mean([point[2] for point in new_utm_link_points1])
elevation_mean2 = np.mean([point[2] for point in new_utm_link_points2])
return abs(elevation_mean1 - elevation_mean2)
def has_overpass(self, topo_feature, lane_edge_features, source_srs):
# 判断这个地方是否是高架桥
utm_topo_point = transform_to_utm(topo_feature.GetGeometryRef().GetPoints(), source_srs)[0][0]
link_id1 = lane_edge_features[0][const.HD_LINK_ID] # 最近的边线的id
mesh_id1 = lane_edge_features[0][const.MESH_ID] # 最近边线的图幅号
if len(lane_edge_features) != 1:
for lane_edge_feature in lane_edge_features:
link_id2 = lane_edge_feature[const.HD_LINK_ID]
if link_id2 != link_id1:
mesh_id2 = lane_edge_feature[const.MESH_ID]
# 找到这两条参考线,并计算高程的差距
link_feature1 = self.get_link_by_id(link_id1, mesh_id1)
link_feature2 = self.get_link_by_id(link_id2, mesh_id2)
if not link_feature1 or not link_feature2:
output_info = "根据id:" + str(link_id1) + "和" + str(mesh_id1) + "或者id:" + \
str(link_id2) + "和" + str(mesh_id2) + "没有获取到参考线,请手动接边"
self.output_info1.append(output_info)
return True
blh_link_points1 = link_feature1.GetGeometryRef().GetPoints()
blh_link_points2 = link_feature2.GetGeometryRef().GetPoints()
utm_link_points1 = interpolation_dataset_3d(transform_to_utm(blh_link_points1, source_srs)[0], 5)
utm_link_points2 = interpolation_dataset_3d(transform_to_utm(blh_link_points2, source_srs)[0], 5)
elevation_bias = self.cal_elevation_bias(utm_topo_point, utm_link_points1, utm_link_points2)
if elevation_bias > const.ELEVATION_THRESHOLD:
output_info = "gps_id为" + str(topo_feature[const.ID]) + \
"附近可能为高架桥或者相隔很近的双向道路,请手动接边"
self.output_info1.append(output_info)
return True
return False
def is_connect_road(self, begin_topo_feature, mesh_id1, mesh_id2, source_srs):
# 根据数据形式,begin_topo和end_topo均位于相对的另一个图幅中
# 方法为根据begin_topo的next字段,不断向后搜索,直到距离topo最近的边界线的图幅号变为mesh_id2为止
lane_edge_features = self.get_lane_edge_intersect_buffer(begin_topo_feature, mesh_id1, mesh_id2, source_srs)
if not lane_edge_features:
return False, []
lane_edge_features.sort(key=lambda x: x[0]) # 按照距离gps点的距离排序
lane_edge_features = [lane_edge_feature[1] for lane_edge_feature in lane_edge_features] # 去除距离
if self.has_overpass(begin_topo_feature, lane_edge_features, source_srs):
return False, []
link_id1 = link_id2 = lane_edge_features[0][const.HD_LINK_ID]
mesh_id = mesh_id1 = lane_edge_features[0][const.MESH_ID]
move_count = 0
while mesh_id1 == mesh_id and move_count < const.MOVE_COUNT_THRESHOLD:
next_count = begin_topo_feature[const.NEXT_COUNT]
if not next_count or int(next_count) != 1:
output_info = "gps_id为" + str(begin_topo_feature[const.ID]) + \
"的gps点的下一个gps点的数量不为1,topo数据异常,请手动接边"
self.output_info1.append(output_info)
return False, []
_, next_topo_id = begin_topo_feature[const.NEXT][1:-1].split(':')
filter = '"' + const.ID + '" = ' + str(next_topo_id)
self.topo_point_layer.SetAttributeFilter(filter)
topo_count = self.topo_point_layer.GetFeatureCount()
if topo_count != 1:
output_info = "根据gps_id:" + str(next_topo_id) + "搜索到的topo点不是一个,topo数据异常,请手动接边"
self.output_info1.append(output_info)
return False, []
begin_topo_feature = self.topo_point_layer.GetNextFeature()
self.topo_point_layer.SetAttributeFilter(None) # 消除属性过滤
self.topo_point_layer.ResetReading()
lane_edge_features = self.get_lane_edge_intersect_buffer(begin_topo_feature, mesh_id1, mesh_id2, source_srs)
if not lane_edge_features:
return False, []
lane_edge_features.sort(key=lambda x: x[0]) # 按照距离gps点的距离排序
lane_edge_features = [lane_edge_feature[1] for lane_edge_feature in lane_edge_features] # 去除距离
if self.has_overpass(begin_topo_feature, lane_edge_features, source_srs):
return False, []
mesh_id, link_id2 = lane_edge_features[0][const.MESH_ID], lane_edge_features[0][const.HD_LINK_ID]
if mesh_id == mesh_id1 and link_id2 != link_id1:
# 同一图幅内找到新的待接边参考线
link_id1 = link_id2
move_count += 1
if move_count >= const.MOVE_COUNT_THRESHOLD:
output_info = "gps_id为:" + str(begin_topo_feature[const.ID]) + "的topo点附近接边的数据距离边界太远,请手动接边"
self.output_info1.append(output_info)
return False, []
if link_id1 == link_id2:
output_info = "id为:" + str(link_id1) + "的参考线上挂接有不同图幅的两条边界线,数据异常,请修改数据后手动接边"
self.output_info1.append(output_info)
return False, []
return True, [begin_topo_feature, link_id1, link_id2]
def get_link_by_id(self, link_id, mesh_id):
filter = '"' + const.LINK_ID + '" = ' + str(link_id) + ' and "' + \
const.MESH_ID + '" = ' + "'" + str(mesh_id) + "'"
self.link_layer.SetAttributeFilter(filter)
link_count = self.link_layer.GetFeatureCount()
if link_count != 1:
output_info = "在图幅号为:" + str(mesh_id) + "中根据参考线id:" + \
str(link_id) + "搜索到的参考线不是一条,数据异常,请修改数据后手动接边"
self.output_info1.append(output_info)
return None
link_feature = self.link_layer.GetNextFeature()
self.link_layer.SetAttributeFilter(None) # 消除属性过滤
self.link_layer.ResetReading()
return link_feature
def get_lane_edge_by_link_id(self, link_id, mesh_id):
# 根据边界线关联的参考线获取边界线
filter = '"' + const.HD_LINK_ID + '" = ' + str(link_id) + ' and "' + const.MESH_ID + '" = ' + "'" + str(
mesh_id) + "'"
self.lane_edge_layer.SetAttributeFilter(filter)
lane_edge_count = self.lane_edge_layer.GetFeatureCount()
if lane_edge_count < 2:
output_info = "在图幅号为:" + str(mesh_id) + "中根据参考线id:" + \
str(link_id) + "搜索到的边界线不足2条,请手动接边"
self.output_info1.append(output_info)
return None
lane_edge_features = []
for _ in range(lane_edge_count):
lane_edge_feature = self.lane_edge_layer.GetNextFeature()
if lane_edge_feature is None:
continue
lane_edge_features.append(lane_edge_feature)
if len(lane_edge_features) < 2:
output_info = "在图幅号为:" + str(mesh_id) + "中根据参考线id:" + \
str(link_id) + "搜索到的边界线不足2条,请手动接边"
self.output_info1.append(output_info)
return None
self.lane_edge_layer.SetAttributeFilter(None)
self.lane_edge_layer.ResetReading()
return lane_edge_features
def get_lane_link_by_link_id(self, link_id, mesh_id):
# 根据中心线关联的参考线寻找中心线
filter = '"' + const.HD_LINK_ID + '" = ' + str(link_id) + ' and "' + const.MESH_ID + '" = ' + "'" + str(
mesh_id) + "'"
self.lane_link_layer.SetAttributeFilter(filter)
lane_link_count = self.lane_link_layer.GetFeatureCount()
if lane_link_count == 0:
output_info = "在图幅号为:" + str(mesh_id) + "中,根据关联的参考线id:" + \
str(link_id) + "没有搜索到中心线,数据异常,请修改数据后手动接边"
self.output_info1.append(output_info)
return None
lane_link_features = []
for _ in range(lane_link_count):
lane_link_feature = self.lane_link_layer.GetNextFeature()
if lane_link_feature is None:
continue
lane_link_features.append(lane_link_feature)
if len(lane_link_features) == 0:
output_info = "在图幅号为:" + str(mesh_id) + "中,根据关联的参考线id:" + \
str(link_id) + "没有搜索到中心线,数据异常,请修改数据后手动接边"
self.output_info1.append(output_info)
return None
self.lane_link_layer.SetAttributeFilter(None)
self.lane_link_layer.ResetReading()
return lane_link_features
def get_link_edge_by_link(self, link_feature, mesh_id):
left_link_edge_id, right_link_edge_id = link_feature[const.LEFT_LINK_EDGE], link_feature[const.RIGHT_LINK_EDGE]
if not left_link_edge_id or not right_link_edge_id:
output_info = "id为:" + str(link_feature[const.LINK_ID]) + \
"的参考线的左道路边缘或者右道路边缘为空,数据异常,请修改收据后手动接边"
self.output_info1.append(output_info)
return []
filter = '"' + const.LINK_EDGE_ID + '" = ' + str(left_link_edge_id) + \
' and "' + const.MESH_ID + '" = ' + "'" + str(mesh_id) + "'"
self.link_edge_layer.SetAttributeFilter(filter)
link_edge_count = self.link_edge_layer.GetFeatureCount()
if link_edge_count != 1:
output_info = "根据id:" + str(left_link_edge_id) + "搜索到的道路边缘不是一条,数据异常,请修改数据后手动接边"
self.output_info1.append(output_info)
return []
left_link_edge_feature = self.link_edge_layer.GetNextFeature()
self.link_edge_layer.SetAttributeFilter(None)
self.link_edge_layer.ResetReading()
filter = '"' + const.LINK_EDGE_ID + '" = ' + str(right_link_edge_id) + \
' and "' + const.MESH_ID + '" = ' + "'" + str(mesh_id) + "'"
self.link_edge_layer.SetAttributeFilter(filter)
link_edge_count = self.link_edge_layer.GetFeatureCount()
if link_edge_count != 1:
output_info = "根据id:" + str(right_link_edge_id) + "搜索到的道路边缘不是一条,请修改数据后手动接边"
self.output_info1.append(output_info)
return []
right_link_edge_feature = self.link_edge_layer.GetNextFeature()
self.link_edge_layer.SetAttributeFilter(None)
self.link_edge_layer.ResetReading()
return [left_link_edge_feature, right_link_edge_feature]
def match_line(self, lines1, lines2, id):
# 对前后得线段集合进行匹配,id为参考线的主键
cal_x_mean = lambda line: np.mean([point[0] for point in line[-const.COMPARISON_NUM2:]])
line_info1 = [[cal_x_mean(lines1[i]), i] for i in range(len(lines1))]
line_info2 = [[cal_x_mean(lines2[j]), j] for j in range(len(lines2))]
line_info1.sort(key=lambda x: x[0])
line_info2.sort(key=lambda x: x[0])
m1, m2 = len(line_info1), len(line_info2)
if abs(line_info1[0][0] - line_info2[0][0]) < 1 and abs(line_info1[-1][0] - line_info2[-1][0]) < 1 and m1 != m2:
# 道路宽度一样,车道数不一样
output_info = "id为:" + str(id) + "的参考线的接边处,两者的道路宽度近似,但是车道数不一样,请手动接边"
self.output_info1.append(output_info)
return False, []
if ((abs(line_info1[0][0] - line_info2[0][0]) < 1 and abs(line_info1[-1][0] - line_info2[-1][0]) > 2) or (
abs(line_info1[-1][0] - line_info2[-1][0]) < 1 and abs(
line_info1[0][0] - line_info2[0][0]) > 2)) and m1 == m2:
# 车道数一样,且起始车道线一样(或者末尾车道线一样),道路宽度不一样
output_info = "id为:" + str(id) + "的参考线的接边处,两者的车道数一样,道路宽度不一样,请手动接边"
self.output_info1.append(output_info)
return False, []
match_list = []
is_nearest_line = lambda x, min_d, lines: (np.array([abs(line[0] - x) for line in lines]) >= min_d).all()
for x1, index1 in line_info1:
d = 1000
index = -1
min_x = -1000
for x2, index2 in line_info2:
d1 = abs(x2 - x1)
if d1 < d:
index = index2
d = d1
min_x = x2
if d < const.CONNECT_THRESHOLD and index != -1 and min_x != -1000 and is_nearest_line(min_x, d, line_info1):
# 判断是不是相互距离最近
match_list.append([index1, index])
if not match_list:
return False, []
return True, match_list
@staticmethod
def cal_forward_direction(cpt_points, topo_feature, *proj_info):
"""
计算前向参考线及其关联数据的方向
计算方式:计算线段首尾点到topo点的距离
1. 如果距离的差值相差不大,就认为y越大越为接边点
2. 如果距离的差值相差挺大,就认为距离topo点最近是接边点
"""
blh_topo_point = topo_feature.GetGeometryRef().GetPoints()[0]
cpt_topo_point = blh_to_cpt([blh_topo_point], *proj_info)[0]
distance1 = two_point_distance_3d(cpt_topo_point, cpt_points[0])
distance2 = two_point_distance_3d(cpt_topo_point, cpt_points[-1])
if abs(distance2 - distance1) > 10:
if distance1 < distance2:
# 首点为接边点,方向反了
line_direction = -1
cpt_points = cpt_points[::-1]
else:
# 尾点为接边点,方向为正
line_direction = 1
else:
# 判断最大的y是在首点还是尾点取得
if cpt_points[-1][1] > cpt_points[0][1]:
line_direction = 1
else:
line_direction = -1
cpt_points = cpt_points[::-1]
return cpt_points, line_direction
@staticmethod
def cal_behind_direction(cpt_points, topo_feature, *proj_info):
"""
计算后向参考线及其关联数据的方向
计算方式:计算线段首尾点到topo点的距离
1. 如果距离的差值相差不大,就认为y越小越为接边点
2. 如果距离的差值相差挺大,就认为距离topo点最近是接边点
"""
blh_topo_point = topo_feature.GetGeometryRef().GetPoints()[0]
cpt_topo_point = blh_to_cpt([blh_topo_point], *proj_info)[0]
distance1 = two_point_distance_3d(cpt_topo_point, cpt_points[0])
distance2 = two_point_distance_3d(cpt_topo_point, cpt_points[-1])
if abs(distance2 - distance1) > 10:
if distance1 <= distance2:
# 首点为接边点,方向反了
line_direction = -1
cpt_points = cpt_points[::-1]
else:
# 尾点为接边点,方向为正
line_direction = 1
else:
# 判断最小的y是在首点还是尾点取得
if cpt_points[-1][1] > cpt_points[0][1]:
line_direction = -1
cpt_points = cpt_points[::-1]
else:
line_direction = 1
return cpt_points, line_direction
@staticmethod
def cal_smoothing_count(cpt_dataset1, cpt_dataset2):
# 根据x值得间距计算平滑次数,两数据都指向接边边界
new_cpt_dataset1 = interpolation_dataset_3d(cpt_dataset1, 3)
new_cpt_dataset2 = interpolation_dataset_3d(cpt_dataset2, 3)
mean_x1 = np.mean([point[0] for point in new_cpt_dataset1[-const.COMPARISON_NUM2:]])
mean_x2 = np.mean([point[0] for point in new_cpt_dataset2[-const.COMPARISON_NUM2:]])
return int(15 + 15 * np.log(1 + abs(mean_x1 - mean_x2)) / np.log(2))
def is_connect_lane_edge(self, topo_feature, link_id1, link_id2, mesh_id1, mesh_id2, connect_boundary, *proj_info):
# 基于cpt坐标系做的判断以及获取的数据
lane_edge_features1 = self.get_lane_edge_by_link_id(link_id1, mesh_id1)
if len(lane_edge_features1) < 2:
output_info = "根据id:" + str(link_id1) + "的参考线搜索到的边界线太少,请手动接边"
self.output_info1.append(output_info)
return False, []
lane_edge_features2 = self.get_lane_edge_by_link_id(link_id2, mesh_id2)
if len(lane_edge_features2) < 2:
output_info = "根据id:" + str(link_id2) + "的参考线搜索到的边界线太少,请手动接边"
self.output_info1.append(output_info)
return False, []
cpt_lane_edge_lines1 = []
lane_edge_directions1 = []
for lane_edge_feature in lane_edge_features1:
lane_edge_points = lane_edge_feature.GetGeometryRef().GetPoints()
cpt_lane_edge_points = blh_to_cpt(lane_edge_points, *proj_info)
new_cpt_lane_edge_points, lane_edge_direction = self.cal_forward_direction(cpt_lane_edge_points,
topo_feature, *proj_info)
cpt_lane_edge_lines1.append(new_cpt_lane_edge_points)
lane_edge_directions1.append(lane_edge_direction)
cpt_lane_edge_lines2 = []
lane_edge_directions2 = []
for lane_edge_feature in lane_edge_features2:
lane_edge_points = lane_edge_feature.GetGeometryRef().GetPoints()
cpt_lane_edge_points = blh_to_cpt(lane_edge_points, *proj_info)
new_cpt_lane_edge_points, lane_edge_direction = self.cal_behind_direction(cpt_lane_edge_points,
topo_feature, *proj_info)
cpt_lane_edge_lines2.append(new_cpt_lane_edge_points)
lane_edge_directions2.append(lane_edge_direction)
flag, lane_edge_match_list = self.match_line(cpt_lane_edge_lines1, cpt_lane_edge_lines2, link_id1)
if not flag:
return False, []
smoothing_count_list = []
lane_edge_match_directions = []
get_middle_point = lambda point1, point2: [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2,
(point1[2] + point2[2]) / 2]
for index1, index2 in lane_edge_match_list:
cpt_lane_edge_line1 = cpt_lane_edge_lines1[index1]
cpt_lane_edge_line2 = cpt_lane_edge_lines2[index2]
connect_boundary.append(get_middle_point(cpt_lane_edge_line1[-1], cpt_lane_edge_line2[-1]))
lane_edge_match_directions.append([lane_edge_directions1[index1], lane_edge_directions2[index2]])
smoothing_count_list.append(self.cal_smoothing_count(cpt_lane_edge_line1, cpt_lane_edge_line2))
return True, [lane_edge_features1, lane_edge_features2, lane_edge_match_directions,
lane_edge_match_list, smoothing_count_list]
def is_connect_lane_link(self, topo_feature, link_id1, link_id2, mesh_id1, mesh_id2, connect_boundary, *proj_info):
# 基于cpt坐标系做的判断以及获取的数据
lane_link_features1 = self.get_lane_link_by_link_id(link_id1, mesh_id1)
if not lane_link_features1:
return False, []
lane_link_features2 = self.get_lane_link_by_link_id(link_id2, mesh_id2)
if not lane_link_features2:
return False, []
cpt_lane_link_lines1 = []
lane_link_directions1 = []
for lane_link_feature in lane_link_features1:
lane_link_points = lane_link_feature.GetGeometryRef().GetPoints()
cpt_lane_link_points = blh_to_cpt(lane_link_points, *proj_info)
new_cpt_lane_link_points, lane_link_direction = self.cal_forward_direction(cpt_lane_link_points,
topo_feature, *proj_info)
cpt_lane_link_lines1.append(new_cpt_lane_link_points)
lane_link_directions1.append(lane_link_direction)
cpt_lane_link_lines2 = []
lane_link_directions2 = []
for lane_link_feature in lane_link_features2:
lane_link_points = lane_link_feature.GetGeometryRef().GetPoints()
cpt_lane_link_points = blh_to_cpt(lane_link_points, *proj_info)
new_cpt_lane_link_points, lane_link_direction = self.cal_behind_direction(cpt_lane_link_points,
topo_feature, *proj_info)
cpt_lane_link_lines2.append(new_cpt_lane_link_points)
lane_link_directions2.append(lane_link_direction)
flag, lane_link_match_list = self.match_line(cpt_lane_link_lines1, cpt_lane_link_lines2, link_id1)
if not flag:
return False, []
smoothing_count_list = []
lane_link_match_directions = []
get_middle_point = lambda point1, point2: [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2,
(point1[2] + point2[2]) / 2]
for index1, index2 in lane_link_match_list:
cpt_lane_link_line1 = cpt_lane_link_lines1[index1]
cpt_lane_link_line2 = cpt_lane_link_lines2[index2]
connect_boundary.append(get_middle_point(cpt_lane_link_line1[-1], cpt_lane_link_line2[-1]))
lane_link_match_directions.append([lane_link_directions1[index1], lane_link_directions2[index2]])
smoothing_count_list.append(self.cal_smoothing_count(cpt_lane_link_line1, cpt_lane_link_line2))
return True, [lane_link_features1, lane_link_features2,
lane_link_match_directions, lane_link_match_list, smoothing_count_list]
def is_connect_link_edge(self, topo_feature, link_feature1, link_feature2,
mesh_id1, mesh_id2, connect_boundary, *proj_info):
link_edge_features1 = self.get_link_edge_by_link(link_feature1, mesh_id1)
if not link_edge_features1:
return False, []
link_edge_features2 = self.get_link_edge_by_link(link_feature2, mesh_id2)
if not link_edge_features2:
return False, []
cpt_link_edge_lines1 = []
link_edge_directions1 = []
for link_edge_feature in link_edge_features1:
link_edge_points = link_edge_feature.GetGeometryRef().GetPoints()
cpt_link_edge_points = blh_to_cpt(link_edge_points, *proj_info)
new_cpt_link_edge_points, link_edge_direction = self.cal_forward_direction(cpt_link_edge_points,
topo_feature, *proj_info)
cpt_link_edge_lines1.append(new_cpt_link_edge_points)
link_edge_directions1.append(link_edge_direction)
cpt_link_edge_lines2 = []
link_edge_directions2 = []
for link_edge_feature in link_edge_features2:
link_edge_points = link_edge_feature.GetGeometryRef().GetPoints()
cpt_link_edge_points = blh_to_cpt(link_edge_points, *proj_info)
new_cpt_link_edge_points, link_edge_direction = self.cal_forward_direction(cpt_link_edge_points,
topo_feature, *proj_info)
cpt_link_edge_lines2.append(new_cpt_link_edge_points)
link_edge_directions2.append(link_edge_direction)
flag, link_edge_match_list = self.match_line(cpt_link_edge_lines1, cpt_link_edge_lines2,
link_feature1[const.LINK_ID])
if not flag:
return False, []
smoothing_count_list = []
link_edge_match_directions = []
get_middle_point = lambda point1, point2: [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2,
(point1[2] + point2[2]) / 2]
for index1, index2 in link_edge_match_list:
cpt_link_edge_line1 = cpt_link_edge_lines1[index1]
cpt_link_edge_line2 = cpt_link_edge_lines2[index2]
connect_boundary.append(get_middle_point(cpt_link_edge_line1[-1], cpt_link_edge_line2[-1]))
link_edge_match_directions.append([link_edge_directions1[index1], link_edge_directions2[index2]])
smoothing_count_list.append(self.cal_smoothing_count(cpt_link_edge_line1, cpt_link_edge_line2))
return True, [link_edge_features1, link_edge_features2,
link_edge_match_directions, link_edge_match_list, smoothing_count_list]
def is_connect_link(self, topo_feature, link_id1, link_id2, mesh_id1, mesh_id2, connect_boundary, *proj_info):
"""
在cpt坐标系下,计算接边处x值得差距,差距不大再计算z值得差距
"""
link_feature1 = self.get_link_by_id(link_id1, mesh_id1)
if not link_feature1:
return False, []
link_feature2 = self.get_link_by_id(link_id2, mesh_id2)
if not link_feature2:
return False, []
link_geom1, link_geom2 = link_feature1.GetGeometryRef(), link_feature2.GetGeometryRef()
if not link_geom1.IsSimple() or not link_geom2.IsSimple():
output_info = "id为:" + str(link_id1) + "或" + str(link_id2) + \
"的参考线的几何不是simple geometry,数据异常,请修改数据后手动接边"
self.output_info1.append(output_info)
return False, []
link_points1, link_points2 = link_geom1.GetPoints(), link_geom2.GetPoints()
cpt_link_points1 = blh_to_cpt(link_points1, *proj_info)
cpt_link_points2 = blh_to_cpt(link_points2, *proj_info)
new_cpt_link_points1, link_direction1 = self.cal_forward_direction(cpt_link_points1, topo_feature, *proj_info)
new_cpt_link_points2, link_direction2 = self.cal_behind_direction(cpt_link_points2, topo_feature, *proj_info)
get_middle_point = lambda point1, point2: [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2,
(point1[2] + point2[2]) / 2]
connect_boundary.append(get_middle_point(new_cpt_link_points1[-1], new_cpt_link_points2[-1]))
interpolation_cpt_link_points1 = interpolation_dataset_3d(new_cpt_link_points1, 4)
interpolation_cpt_link_points2 = interpolation_dataset_3d(new_cpt_link_points2, 4)
compare_x_list1 = [point[0] for point in interpolation_cpt_link_points1[-const.COMPARISON_NUM1:]]
compare_x_list2 = [point[0] for point in interpolation_cpt_link_points2[-const.COMPARISON_NUM1:]]
x_mean1, x_mean2 = np.mean(compare_x_list1), np.mean(compare_x_list2)
if abs(x_mean1 - x_mean2) > const.LINK_CONNECT_INTERVAL:
output_info = "id为:" + str(link_id1) + "和" + str(link_id2) + "的参考线间距太大,请手动接边"
self.output_info1.append(output_info)
return False, []
compare_z_list1 = [point[2] for point in interpolation_cpt_link_points1[-const.COMPARISON_NUM1:]]
compare_z_list2 = [point[2] for point in interpolation_cpt_link_points2[-const.COMPARISON_NUM1:]]
if abs(np.mean(compare_z_list1) - np.mean(compare_z_list2)) > const.ELEVATION_THRESHOLD:
output_info = "id为:" + str(link_id1) + "和" + str(link_id2) + "的参考线的高程值差距太大,数据异常,请手动接边"
self.output_info1.append(output_info)
return False, []
smoothing_count = min(max(15, int(abs(x_mean1 - x_mean2) * const.SMOOTHING_COUNT)), 70) # 15到70之间
return True, [link_feature1, link_feature2, link_direction1, link_direction2, smoothing_count]
@staticmethod
def remove_near_points(dataset):
"""
去除相隔很近的数据
:param dataset: utm坐标系下的数据仅计算x, y坐标
:return: 返回的是utm坐标系下的点集
"""
dataset1 = dataset[::-1] + []
target_dataset = []
point1 = dataset1.pop()
target_dataset.append(point1)
while dataset1:
point2 = dataset1.pop()
distance = two_point_distance_2d(point1, point2)
if distance < 0.03:
# 两点的距离小于3cm,就删除一个点
continue
target_dataset.append(point2)
point1 = point2
return target_dataset
@staticmethod
def norm_dataset(dataset, point):
"""
规范化接边的数据
:param dataset: utm坐标系下的点集
:param point: 接边边界上的一个点
:return: 返回一个点集,且在utm坐标系下不与接边边界相交
"""
point1 = dataset.pop()
if is_same_point_3d(point1, point):
point1 = dataset.pop()
while dataset:
point2 = dataset.pop()
if is_same_point_3d(point2, point):
if dataset:
point2 = dataset.pop()
else:
break
result = three_point_dot_product(point2, point1, point)
if result >= 0:
point1 = point2
else:
dataset.append(point2)
dataset.append(point1)
break
return dataset
def get_standard_dataset(self, utm_dataset1, utm_dataset2, utm_boundary,
smoothing_count, source_srs, utm_srs, gps_id):
"""
在utm坐标系下,将对应的接边数据标准化,操作流程:
1. 剔除相距很近的点,然后插值
2. 寻找与边界的交点
3. 规范化数据,使得接边的线不与边界相交
4. 将前后数据连接前来进行平滑,之后抽稀,之后去除相隔很近的点
:param self:
:param utm_dataset1: 前向数据投影到utm坐标系
:param utm_dataset2: 后向数据投影到utm坐标系
:param utm_boundary: 接边边界,utm坐标系下
:param gps_id: 接边附近的topo点的主键,主要用来返回错误信息
:param utm_srs: utm的坐标系
:param source_srs: 图层自带的坐标系
:param smoothing_count: 平滑次数
:return: 返回标准的两个点集数据
"""
# 先删除很近的点,再给数据插值,避免造成异常,然后截取部分数据,仅操作一小段数据
if not utm_dataset1 or not utm_dataset2:
output_info = "gps_id为:" + str(gps_id) + "的topo点附近的接边线为空"
self.output_info1.append(output_info)
return False, []
utm_dataset1, utm_dataset2 = self.remove_near_points(utm_dataset1), self.remove_near_points(utm_dataset2)
interpol_utm_dataset1 = interpolation_dataset_3d(utm_dataset1, 3) # 按每米3个点进行插值
interpol_utm_dataset2 = interpolation_dataset_3d(utm_dataset2, 3)
compare_dataset1 = interpol_utm_dataset1[-const.COMPARISON_NUM2:]
intersection_point1 = intersection_of_two_line(compare_dataset1[0], compare_dataset1[-1],
utm_boundary[0], utm_boundary[-1])
if not intersection_point1:
output_info = "gps_id为:" + str(gps_id) + "的topo点附近的接边线与接边边界平行,数据反常,请手动接边"
self.output_info1.append(output_info)
return False, []
compare_dataset2 = interpol_utm_dataset2[-const.COMPARISON_NUM2:]
intersection_point2 = intersection_of_two_line(compare_dataset2[0], compare_dataset2[-1],
utm_boundary[0], utm_boundary[-1])
if not intersection_point2:
output_info = "gps_id为:" + str(gps_id) + "的topo点附近的接边线与接边边界平行,数据反常,请手动接边"
self.output_info1.append(output_info)
return False, []
new_utm_dataset1 = self.norm_dataset(interpol_utm_dataset1, intersection_point1)
new_utm_dataset2 = self.norm_dataset(interpol_utm_dataset2, intersection_point2)
if len(new_utm_dataset1) < 2 or len(new_utm_dataset2) < 2:
output_info = "gps_id为:" + str(gps_id) + "的topo点附近的接边线的大部分超越了接边边界,数据反常,请手动接边"
self.output_info1.append(output_info)
return False, []
# 由于前面已经插了值,50cm一个点,选取定长的数据可以直接获取
operate_point_num = 20 # 选择操作点的数量,20*0.5表示操作点的长度
# 选择20 * 0.5=10米的数据进行操作,但只取前5个点,避免两条线间距较大时获取的平滑结果有异常时两个点
new_compare_dataset1 = new_utm_dataset1[-operate_point_num:][:5]
if len(new_utm_dataset1) - operate_point_num <= 0:
new_utm_dataset1 = []
else:
new_utm_dataset1 = new_utm_dataset1[:len(new_utm_dataset1) - operate_point_num] # 截取数据,仅操作部分数据
new_compare_dataset2 = new_utm_dataset2[-operate_point_num:][:5]
if len(new_utm_dataset2) - operate_point_num <= 0:
new_utm_dataset2 = []
else:
new_utm_dataset2 = new_utm_dataset2[:len(new_utm_dataset2) - operate_point_num]
compare_dataset = interpolation_dataset_3d(new_compare_dataset1 + new_compare_dataset2[::-1], 2)
flag, info = self.smoothing_dataset(compare_dataset, smoothing_count, utm_boundary, gps_id)
if not flag:
return False, []
# 街边附近返回的数据已经按照指定阈值插值
target_compare_dataset1, target_compare_dataset2, intersection_point = info
if len(new_utm_dataset1) > 1:
new_utm_dataset1 = ImprovedDouglasPeucker(const.INTERPOLATION_COUNT4).main(new_utm_dataset1)
if len(new_utm_dataset2) > 1:
new_utm_dataset2 = ImprovedDouglasPeucker(const.INTERPOLATION_COUNT4).main(new_utm_dataset2)
target_utm_dataset1 = new_utm_dataset1 + target_compare_dataset1
target_utm_dataset2 = new_utm_dataset2 + target_compare_dataset2
blh_intersection_point = transform_to_src([intersection_point], source_srs, utm_srs)[0]
target_blh_dataset1 = transform_to_src(target_utm_dataset1, source_srs, utm_srs)
target_blh_dataset2 = transform_to_src(target_utm_dataset2, source_srs, utm_srs)
target_blh_dataset1.append(blh_intersection_point)
target_blh_dataset2.append(blh_intersection_point)
if len(target_blh_dataset1) > 1:
target_blh_dataset1 = ImprovedDouglasPeucker(const.INTERPOLATION_COUNT4).main(target_blh_dataset1)
if len(target_blh_dataset2) > 1:
target_blh_dataset2 = ImprovedDouglasPeucker(const.INTERPOLATION_COUNT4).main(target_blh_dataset2)
return True, [target_blh_dataset1, target_blh_dataset2, blh_intersection_point]
@staticmethod
def is_intersect_in_line(point1, point2, point3, point4):
# point1和point2为一条线的起终点,point3和point4为一条直线的起终点
# 判断两条线段的交点是否在第一条线上
intersect_point = intersection_of_two_line(point1, point2, point3, point4)
if not intersect_point:
# 两条线平行,没有交点
return False
if three_point_dot_product(point1, intersect_point, point2) > 0:
return False
return True
def smoothing_dataset(self, compare_points, smoothing_count, connect_boundary, gps_id):
"""
对接边的数据进行处理:平滑、抽稀、分组,求交点
此处的抽稀中包含了抽稀后按照指定阈值插值
:param compare_points: 需处理的原始数据,已经插值了
:param smoothing_count: 平滑的次数
:param connect_boundary: 接边的边界
:param gps_id: 接边附近某个topo点的主键
:return:
"""
smoothing_dataset = SavitzkyGolayFilter().main(compare_points, smoothing_count)
vacuate_dataset = ImprovedDouglasPeucker(const.INTERPOLATION_COUNT4).main(smoothing_dataset)
target_dataset = self.remove_near_points(vacuate_dataset)
new_dataset1, new_dataset2 = [], []
for i in range(len(target_dataset) - 1):
if self.is_intersect_in_line(target_dataset[i], target_dataset[i + 1],
connect_boundary[0], connect_boundary[-1]):
new_dataset1 = target_dataset[:i + 1]
new_dataset2 = target_dataset[i + 1:][::-1]
break
if not new_dataset1 or not new_dataset2:
output_info = "gps_id为:" + str(gps_id) + "的topo点附近的接边线的大部分超越了接边边界,数据反常,请手动接边"
self.output_info1.append(output_info)
return False, []
intersection_point = intersection_of_two_line(new_dataset1[-1], new_dataset2[-1],
connect_boundary[0], connect_boundary[-1])
if not intersection_point:
output_info = "gps_id为:" + str(gps_id) + "的topo点附近的接边线与接边边界平行,数据反常,请手动接边"
self.output_info1.append(output_info)
return False, []
return True, [new_dataset1, new_dataset2, intersection_point]
@staticmethod
def get_boundary_point(point, x_mean, y_mean, theta, parameter):
# 将一个utm坐标系下的点投影到局部坐标系下,然后计算其对应的拟合值为多少,再反投影到utm坐标系下
new_point = [point[0] - x_mean, point[1] - y_mean]
positive_point = PosNegTrans().positive_transformation_point(theta, new_point)
X_vector = PolyFit().get_coefficient_vector(positive_point, 1)
Y_predict = float(np.squeeze(np.dot(X_vector.T, parameter))) # 去掉矩阵的维度,变为一个浮点数
negative_point = np.zeros((2, 1))
negative_point[0, 0], negative_point[1, 0] = positive_point[0], Y_predict
result_point = PosNegTrans().negative_transformation_point(theta, negative_point)
target_point = [result_point[0] + x_mean, result_point[1] + y_mean]
return target_point
def get_connect_boundary(self, cpt_connect_boundary, source_srs, *proj_info):
"""
获取两个图幅文件对应接边的两条道路的共有边界
:param self:
:param cpt_connect_boundary: cpt坐标系下拟合分割线的点集
:param source_srs: 原始图层自带的坐标系
:param proj_info: 投影相关的参数,三个姿态角以及自身经纬高
:return: utm坐标系下的分割线
"""
blh_points = cpt_to_blh(cpt_connect_boundary, *proj_info)
utm_points = transform_to_utm(blh_points, source_srs)[0]
# 对获取的交界的点集做直线拟合,获取边界
positive_dataset, theta, x_mean, y_mean = PosNegTrans().positive_transformation(utm_points)
parameter = PolyFit().poly_fitting_parameter(positive_dataset, 1)
if parameter.shape == (1,):
# 出现奇异矩阵的时候,默认损失为无穷
output_info = "接边中点点集" + str(cpt_connect_boundary) + "做直线拟合过程中,系数矩阵奇异,请修改数据后手动接边"
self.output_info1.append(output_info)
return False, []
# 取左边图幅第一条边界西安
target_point1 = self.get_boundary_point(utm_points[0], x_mean, y_mean, theta, parameter)
new_target_point1 = [target_point1[0], target_point1[1], utm_points[0][2]]
target_point2 = self.get_boundary_point(utm_points[-1], x_mean, y_mean, theta, parameter)
new_target_point2 = [target_point2[0], target_point2[1], utm_points[0][2]]
return True, [new_target_point1, new_target_point2]
def get_node_by_id(self, node_layer, node_id_field, node_id, mesh_id, require_node_point, layer_name, source_srs):
"""
根据id搜索节点,并判断节点坐标是否正确
:param node_layer: 节点图层
:param node_id_field: 节点的主键名
:param node_id: 节点id
:param mesh_id: 图幅号
:param require_node_point: 节点正确的坐标,坐标系为经纬高
:param layer_name: 对应的线图层的名字
:param source_srs: 原始的坐标系的系统
:return: 返回boolean, feature
"""
filter = '"' + node_id_field + '" = ' + str(node_id) + ' and "' + \
const.MESH_ID + '" = ' + "'" + str(mesh_id) + "'"
node_layer.SetAttributeFilter(filter)
node_count = node_layer.GetFeatureCount()
if node_count != 1:
output_info = "根据id为:" + str(node_id) + "寻找" + str(layer_name) + "节点失败,请修改数据后手动接边"
self.output_info1.append(output_info)
node_layer.SetAttributeFilter(None) # 消除属性过滤
return False, None
node_feature = node_layer.GetNextFeature()
node_point = node_feature.GetGeometryRef().GetPoints()[0]
node_layer.SetAttributeFilter(None) # 消除属性过滤
node_layer.ResetReading()
utm_require_node_point = transform_to_utm([require_node_point], source_srs)[0][0]
utm_node_point = transform_to_utm([node_point], source_srs)[0][0]
if is_same_point_3d(utm_node_point, utm_require_node_point):
return True, node_feature
else:
output_info = "节点id为:" + str(node_id) + "的" + str(layer_name) + "节点不与" + \
str(layer_name) + "的端点重合,请修改数据后手动接边"
self.output_info1.append(output_info)
return False, None
def connect_point(self, line_feature, ori_node_point, line_direction, blh_node_point,
node_layer, node_fields, layer_name, source_srs):
"""
连接线图层对应的点图层
:param line_feature: 线feature
:param ori_node_point: 原始节点应该的坐标
:param line_direction: 线的方向
:param blh_node_point: 节点
:param node_layer:
:param node_fields: 长度为3的列表,第一个表示线的起点字段,第二个表示线的终点字段,第三个表示节点的主键名
:param layer_name: 对应线图层的名字
:param source_srs: 原始坐标系系统
:return:
"""
mesh_id = line_feature[const.MESH_ID]
if line_direction == -1:
node_id = line_feature[node_fields[0]] # 获取首点id
else:
node_id = line_feature[node_fields[1]] # 获取尾点id
flag, node_feature = self.get_node_by_id(node_layer, node_fields[2], node_id,
mesh_id, ori_node_point, layer_name, source_srs)
if not flag:
return False
geom = create_point_geometry(blh_node_point)
node_feature.SetGeometry(geom)
node_layer.SetFeature(node_feature)
node_feature.Destroy()
return True
def is_intersect_by_multi_lines(self, node_layer, blh_point, mesh_id, gps_id, layer_name, source_srs):
# 判断一个点处是否是多条线的端点,如在路口或者匝道处
lon, lat = blh_point[:2]
buffer_size = 0.0000001
min_lon, max_lon = lon - buffer_size, lon + buffer_size
min_lat, max_lat = lat - buffer_size, lat + buffer_size
node_layer.SetSpatialFilterRect(min_lon, min_lat, max_lon, max_lat)
node_count = node_layer.GetFeatureCount()
if node_count == 1:
node_layer.SetSpatialFilter(None)
return False
filter = '"' + const.MESH_ID + '" = ' + "'" + str(mesh_id) + "'"
node_layer.SetAttributeFilter(filter)
node_count = node_layer.GetFeatureCount()
if node_count == 1:
node_layer.SetSpatialFilter(None) # 消除空间过滤
node_layer.SetAttributeFilter(None) # 消除属性过滤
return False
# node_points = [node_layer.GetNextFeature().GetGeometryRef().GetPoints()[0] for _ in range(node_count)]
# 使用推导式会出现奇怪的错误
node_points = []
for _ in range(node_count):
node_feature = node_layer.GetNextFeature()
if not node_feature:
continue
node_point = node_feature.GetGeometryRef().GetPoints()[0]
node_points.append(node_point)
utm_node_points = transform_to_utm(node_points, source_srs)[0]
utm_point = transform_to_utm([blh_point], source_srs)[0][0]
node_layer.SetSpatialFilter(None) # 消除空间过滤
node_layer.SetAttributeFilter(None) # 消除属性过滤
node_layer.ResetReading()
same_point_num = 0
for point1 in utm_node_points:
if point1 and is_same_point_3d(utm_point, point1):
same_point_num += 1
if same_point_num >= 2:
output_info = "gps_id:" + str(gps_id) + ",mesh_id:" + str(mesh_id) + "的topo点附近的接边处," + \
str(layer_name) + "的节点坐标为:" + str(blh_point) + ",在此处有2条线共点,请修改数据再接边"
self.output_info1.append(output_info)
return True
def connect_line(self, source_srs, utm_boundary, gps_id, attribute_info, connect_info):
"""
连接线图层以及对应的点图层,先连接线图层,再连接点图层
:param source_srs: 图层的坐标系
:param utm_boundary: 接边的边界
:param gps_id: 接边附近的一个topo点
:param attribute_info: 接边的属性信息
:param connect_info: 接边的连接信息,根据计算获取
:return: boolean
"""
if not attribute_info or not connect_info:
return False
line_layer, node_layer, node_fields, layer_name = attribute_info
features1, features2, match_directions, match_list, smoothing_count_list = connect_info
if line_layer is None:
# 没有边缘图层时,直接运行结束
return False
for i in range(len(match_list)):
index1, index2 = match_list[i]
smoothing_count = smoothing_count_list[i]
feature1, feature2 = features1[index1], features2[index2]
direction1, direction2 = match_directions[i]
blh_points1 = feature1.GetGeometryRef().GetPoints()
blh_points2 = feature2.GetGeometryRef().GetPoints()
utm_points1, utm_srs = transform_to_utm(blh_points1, source_srs)
utm_points2 = transform_to_utm(blh_points2, source_srs)[0]
cal_line_length = lambda line: sum([two_point_distance_2d(line[j], line[j + 1]) for j in range(len(line) - 1)])
length1, length2 = cal_line_length(utm_points1), cal_line_length(utm_points2)
if length1 < 3 or length2 < 3:
output_info = "gps_id为:" + str(gps_id) + "附近的接边处," + \
str(layer_name) + "的两条线至少有一条长度太短,自动接边有风险,请手动接边"
self.output_info1.append(output_info)
return False
if direction1 == -1:
utm_points1 = utm_points1[::-1]
blh_points1 = blh_points1[::-1]
if direction2 == -1:
utm_points2 = utm_points2[::-1]
blh_points2 = blh_points2[::-1]
flag1 = self.is_intersect_by_multi_lines(node_layer, blh_points1[-1], feature1[const.MESH_ID],
gps_id, layer_name, source_srs)
flag2 = self.is_intersect_by_multi_lines(node_layer, blh_points2[-1], feature2[const.MESH_ID],
gps_id, layer_name, source_srs)
if flag1 or flag2:
return False
flag, connect_info = self.get_standard_dataset(utm_points1, utm_points2, utm_boundary,
smoothing_count, source_srs, utm_srs, gps_id)
if not flag:
return False
target_blh_dataset1, target_blh_dataset2, blh_intersection_point = connect_info
if direction1 == -1:
target_blh_dataset1 = target_blh_dataset1[::-1]
if direction2 == -1:
target_blh_dataset2 = target_blh_dataset2[::-1]
geom1 = create_line_geometry(target_blh_dataset1)
geom2 = create_line_geometry(target_blh_dataset2)
feature1.SetGeometry(geom1)
feature2.SetGeometry(geom2)
line_layer.SetFeature(feature1)
line_layer.SetFeature(feature2)
# 连接线图层对应的节点图层
if not self.connect_point(feature1, blh_points1[-1], direction1, blh_intersection_point,
node_layer, node_fields, layer_name, source_srs) or \
not self.connect_point(feature2, blh_points2[-1], direction2, blh_intersection_point,
node_layer, node_fields, layer_name, source_srs):
output_info = "gps_id为:" + str(gps_id) + "附近的接边处," + \
str(layer_name) + "的节点没有连接成功,请手动接边"
self.output_info1.append(output_info)
return False
return True
@staticmethod
def reset_mesh_id(topo_feature, mesh_id1, mesh_id2):
# 根据topo点的信息,重新设置图幅号,按照采集车的行驶轨迹,确认前后的图幅号
mesh_id = topo_feature["MESHID"]
if mesh_id == mesh_id1:
mesh_id1, mesh_id2 = mesh_id2, mesh_id1
return mesh_id1, mesh_id2
def models(self, topo_features, mesh_id1, mesh_id2):
source_srs = self.link_layer.GetSpatialRef() # 获取图层的坐标系
for topo_feature in topo_features:
try:
link_ids = [] # 存储已经接过边的道路参考线id,主要应用于多次同路轨迹
mesh_id1, mesh_id2 = self.reset_mesh_id(topo_feature, mesh_id1, mesh_id2)
if self.is_gap_or_big_curve(topo_feature, source_srs):
# 大弯道或者大间断
continue
flag, connect_info = self.is_connect_road(topo_feature, mesh_id1, mesh_id2, source_srs)
if not flag:
continue
connect_topo_feature, link_id1, link_id2 = connect_info # connect_topo_feature表示连接处的topo点
if link_id1 in link_ids and link_id2 in link_ids:
# 此处已经接过边了,不需要重复接边
continue
gps_id = connect_topo_feature[const.ID] # topo点的主键
lon, lat, up = connect_topo_feature.GetGeometryRef().GetPoints()[0]
pitch = float(connect_topo_feature[const.PITCH])
roll = float(connect_topo_feature[const.ROLL])
azimuth = float(connect_topo_feature[const.AZIMUTH])
proj_info = [lon, lat, up, pitch, roll, azimuth]
cpt_connect_boundary = []
flag1, link_info = self.is_connect_link(connect_topo_feature, link_id1, link_id2, mesh_id1,
mesh_id2, cpt_connect_boundary, *proj_info)
if not flag1:
continue
link_feature1, link_feature2, link_direction1, link_direction2, link_smoothing_count = link_info
flag2, lane_edge_connect_info = self.is_connect_lane_edge(connect_topo_feature, link_id1, link_id2,
mesh_id1, mesh_id2, cpt_connect_boundary,
*proj_info)
if not flag2:
continue
flag3, lane_link_connect_info = self.is_connect_lane_link(connect_topo_feature, link_id1, link_id2,
mesh_id1, mesh_id2, cpt_connect_boundary,
*proj_info)
if not flag3:
continue
link_edge_connect_info = None
if self.link_edge_layer:
flag4, link_edge_connect_info = self.is_connect_link_edge(connect_topo_feature, link_feature1,
link_feature2, mesh_id1, mesh_id2,
cpt_connect_boundary, *proj_info)
if not flag4:
continue
if len(cpt_connect_boundary) < 2:
output_info = "gps_id为:" + str(gps_id) + "的gps附近的接边数据的边界点太少,数据异常,请修改数据后接边"
self.output_info1.append(output_info)
continue
flag5, utm_connect_boundary = self.get_connect_boundary(cpt_connect_boundary, source_srs, *proj_info)
if not flag5:
continue
# 连接参考线及其节点
link_attribute_info = [self.link_layer, self.node_layer,
[const.START_NODE_ID, const.END_NODE_ID, const.NODE_ID], const.HD_LINK]
link_connect_info = [[link_feature1], [link_feature2], [[link_direction1, link_direction2]], [[0, 0]],
[link_smoothing_count]]
if self.connect_line(source_srs, utm_connect_boundary, gps_id, link_attribute_info, link_connect_info):
output_info = "gps_id为:" + str(gps_id) + "的topo的接边处,参考线接边成功"
self.output_info2.append(output_info)
else:
output_info = "gps_id为:" + str(gps_id) + "的topo的接边处,参考线接边失败,请手动接边"
self.output_info1.append(output_info)
continue
# 连接边界线及其节点
lane_edge_attribute_info = [self.lane_edge_layer, self.lane_edge_node_layer,
[const.START_EDGE_NODE_ID, const.END_EDGE_NODE_ID, const.LANE_EDGE_NODE_ID],
const.HD_LANE_EDGE]
if self.connect_line(source_srs, utm_connect_boundary, gps_id,
lane_edge_attribute_info, lane_edge_connect_info):
output_info = "gps_id为:" + str(gps_id) + "的topo的接边处,边界线接边成功"
self.output_info2.append(output_info)
else:
output_info = "gps_id为:" + str(gps_id) + "的topo的接边处,边界线接边失败,请手动接边"
self.output_info1.append(output_info)
continue
# 连接中心线及其节点
lane_link_attribute_info = [self.lane_link_layer, self.lane_node_layer,
[const.START_LANE_NODE_ID, const.END_LANE_NODE_ID, const.LANE_NODE_ID],
const.HD_LANE_LINK]
if self.connect_line(source_srs, utm_connect_boundary, gps_id,
lane_link_attribute_info, lane_link_connect_info):
output_info = "gps_id为:" + str(gps_id) + "的topo的接边处,中心线接边成功"
self.output_info2.append(output_info)
else:
output_info = "gps_id为:" + str(gps_id) + "的topo的接边处,中心线接边失败,请手动接边"
self.output_info1.append(output_info)
continue
# 连接道路边缘及其节点
link_edge_attribute_info = [self.link_edge_layer, self.link_edge_node_layer,
[const.START_EDGE_NODE_ID, const.END_EDGE_NODE_ID, const.LINK_EDGE_NODE_ID],
const.HD_LINK_EDGE]
if self.connect_line(source_srs, utm_connect_boundary,
gps_id, link_edge_attribute_info, link_edge_connect_info):
output_info = "gps_id为:" + str(gps_id) + "的topo的接边处,道路边缘接边成功"
self.output_info2.append(output_info)
else:
if self.link_edge_layer:
output_info = "gps_id为:" + str(gps_id) + "的topo的接边处,道路边缘接边失败,请手动接边"
self.output_info1.append(output_info)
continue
# 运行到此处,说明所有该接边的图层都接边了
link_ids.append(link_id1)
link_ids.append(link_id2)
except Exception as e:
output_info = "在gps_id为:" + str(topo_feature[const.ID]) + \
"附近的接边处,接边程序出发异常,异常为:" + str(e) + "请手动接边"
self.output_info1.append(output_info)
continue
# _*_ coding: utf-8 _*_ #
# @Time: 2020/4/11 17:47
# @Author: XiongChao
import numpy as np
ep2 = 0.0067394967407662064 # 第二偏心率平方 ep2 = ep * ep = (a*a - b*b)/b*b
e2 = 0.006694379988651241 # 第一偏心率平方e2 = e * e = (a * a - b * b) / a * a
c = 6399593.6257536924 # c是为简化书写引入的符号,c = a*a/b;
def radian_to_degree(radian):
# 将弧度转化为角度
return radian * 180 / np.pi
def degree_to_radian(degree):
# 将角度转变为弧度
return degree / 180 * np.pi
def get_n(radian_b):
# 获取某维度,radian_b是纬度的弧度表达
cos_b = np.cos(radian_b)
v = np.sqrt(1 + ep2 * cos_b * cos_b)
n = c / v
return n
def blh_to_ecef(lon, lat, up):
# 经纬高转空间直角坐标系
radian_b = lat / 180 * np.pi # 纬度
radian_l = lon / 180 * np.pi
cos_b = np.cos(radian_b)
sin_b = np.sin(radian_b)
cos_l = np.cos(radian_l)
sin_l = np.sin(radian_l)
n = get_n(radian_b)
x = (n + up) * cos_b * cos_l
y = (n + up) * cos_b * sin_l
z = (n * (1 - e2) + up) * sin_b
return [x, y, z]
def ecef_to_blh(x, y, z):
lon_radian = np.arctan(y / x)
if lon_radian < 0:
lon_radian += np.pi
lon = radian_to_degree(lon_radian)
sqrt_xy = np.sqrt(x * x + y * y)
t0 = z / sqrt_xy
p = c * e2 / sqrt_xy
k = 1 + ep2
# 迭代计算纬度
ti = t0
ti1 = 0
loop = 0
while loop < 10000:
loop += 1
ti1 = t0 + p * ti / np.sqrt(k + ti * ti)
if np.abs(ti1 - ti) < 1e-12:
break
ti = ti1
b_radian = np.arctan(ti1)
n = get_n(b_radian)
h = sqrt_xy / np.cos(b_radian) - n
b = radian_to_degree(b_radian)
return [lon, b, h]
def construct_matrix_cpt_enu(roll, pitch, azimuth):
radian_azimuth = degree_to_radian(azimuth)
radian_pitch = degree_to_radian(pitch)
radian_roll = degree_to_radian(roll)
azimuth_info = [np.cos(radian_azimuth), np.sin(radian_azimuth), 0,
-np.sin(radian_azimuth), np.cos(radian_azimuth), 0,
0, 0, 1]
matrix_azimuth = np.array(azimuth_info).reshape((3, 3))
pitch_info = [1, 0, 0,
0, np.cos(radian_pitch), -np.sin(radian_pitch),
0, np.sin(radian_pitch), np.cos(radian_pitch)]
matrix_pitch = np.array(pitch_info).reshape((3, 3))
roll_info = [np.cos(radian_roll), 0, np.sin(radian_roll),
0, 1, 0,
-np.sin(radian_roll), 0, np.cos(radian_roll)]
matrix_roll = np.array(roll_info).reshape((3, 3))
return np.dot(matrix_azimuth, np.dot(matrix_pitch, matrix_roll))
def construct_matrix_enu_cpt(roll, pitch, azimuth):
# 利用正交阵的转置就是它的逆
return construct_matrix_cpt_enu(roll, pitch, azimuth).T
def construct_matrix_enu_ecef(lon, lat):
radian_lon = degree_to_radian(lon)
radian_lat = degree_to_radian(lat)
info = [-np.sin(radian_lon), -np.cos(radian_lon) * np.sin(radian_lat), np.cos(radian_lon) * np.cos(radian_lat),
np.cos(radian_lon), -np.sin(radian_lon) * np.sin(radian_lat), np.sin(radian_lon) * np.cos(radian_lat),
0, np.cos(radian_lat), np.sin(radian_lat)]
return np.array(info).reshape((3, 3))
def construct_matrix_ecef_enu(lon, lat):
return construct_matrix_enu_ecef(lon, lat).T
def construct_vector_enu_ecef(lon, lat, up):
# 计算当前cpt原点在ecef坐标系中的坐标
ecef_point = blh_to_ecef(lon, lat, up)
return np.array(ecef_point).reshape((3, 1))
def enu_to_cpt(enu_points_matrix, pitch, roll, azimuth):
# 将enu上的点投影到cpt坐标系上,enu_points_matrix的每一列表示一个点
return np.dot(construct_matrix_enu_cpt(roll, pitch, azimuth), enu_points_matrix)
def cpt_to_enu(cpt_points_matrix, pitch, roll, azimuth):
# 将cpt上的点投影到enu坐标系上,cpt_points_matrix的每一列表示一个点
return np.dot(construct_matrix_cpt_enu(roll, pitch, azimuth), cpt_points_matrix)
def ecef_to_enu(ecef_points_matrix, lon, lat, up):
# 将ecef上的某点投影到指定的enu坐标系上,ecef_points_matrix的每一列表示一个点
translation_vector = construct_vector_enu_ecef(lon, lat, up)
return np.dot(construct_matrix_ecef_enu(lon, lat), (ecef_points_matrix - translation_vector))
def enu_to_ecef(enu_points_matrix, lon, lat, up):
# 将enu上的某点投影到指定的ecef坐标系上,enu_points_matrix的每一列表示一个点
translation_vector = construct_vector_enu_ecef(lon, lat, up)
return np.dot(construct_matrix_enu_ecef(lon, lat), enu_points_matrix) + translation_vector
def ecef_to_cpt(lon, lat, up, pitch, roll, azimuth, ecef_points):
# 将ecef上的点投影到指定的cpt坐标系上
# 范围的是一个二维列表,每一个元素为一个cpt坐标系下的点
ecef_points_matrix = np.array(ecef_points).T
enu_points_matrix = ecef_to_enu(ecef_points_matrix, lon, lat, up)
cpt_points_matrix = enu_to_cpt(enu_points_matrix, pitch, roll, azimuth)
cpt_points = [cpt_points_matrix[:, i] for i in range(np.shape(cpt_points_matrix)[-1])]
return cpt_points
def cpt_to_ecef(lon, lat, up, pitch, roll, azimuth, cpt_points):
# 将cpt上的点投影到指定的ecef坐标系上
# cpt_points是一个二维列表,每一个元素表示一个cpt下的点,[[x, y, z], ...]
# 返回的是一个二维列表,每一个元素表示一个ecef坐标系下的点
cpt_points_matrix = np.array(cpt_points).T
enu_points_matrix = cpt_to_enu(cpt_points_matrix, pitch, roll, azimuth)
ecef_points_matrix = enu_to_ecef(enu_points_matrix, lon, lat, up)
return [ecef_points_matrix[:, i] for i in range(np.shape(ecef_points_matrix)[-1])]
def blh_to_cpt(blh_points, lon, lat, up, pitch, roll, azimuth):
ecef_points = [blh_to_ecef(point[0], point[1], point[2]) for point in blh_points]
ecef_points_matrix = np.array(ecef_points).T
enu_points_matrix = ecef_to_enu(ecef_points_matrix, lon, lat, up)
cpt_points_matrix = enu_to_cpt(enu_points_matrix, pitch, roll, azimuth)
cpt_points = [cpt_points_matrix[:, i] for i in range(np.shape(cpt_points_matrix)[-1])]
return cpt_points
def cpt_to_blh(cpt_points, lon, lat, up, pitch, roll, azimuth):
# 将cpt坐标系下的点投影到blh坐标系下
# cpt_points是一个二维列表,每一个元素表示一个cpt坐标系下的点
# 返回的是一个二维列表,每一个元素表示一个blh坐标系下的点
cpt_points_matrix = np.array(cpt_points).T
enu_points_matrix = cpt_to_enu(cpt_points_matrix, pitch, roll, azimuth)
ecef_points_matrix = enu_to_ecef(enu_points_matrix, lon, lat, up)
blh_points = [ecef_to_blh(*ecef_points_matrix[:, i]) for i in range(np.shape(ecef_points_matrix)[-1])]
return blh_points
# if __name__ == '__main__':
# blh_points = [(121.29252568495818, 30.2123505136701, 28.342482462525368),
# (121.29250801166651, 30.21231321905551, 28.37178515084088),
# (121.29249033838757, 30.212275924438828, 28.40108783915639),
# (121.29247282234871, 30.212238824265214, 28.429960872977972),
# (121.2924553063224, 30.21220172408956, 28.458833906799555),
# (121.29243939266972, 30.212167895917432, 28.48491994710639),
# (121.29242347902743, 30.212134067743627, 28.511005987413228),
# ]
# cpt_points = [[-4.78917141, -1.34333042, -1.67376305], [-4.88939957, -4.30204398, -1.74877275]]
# cpt_points = [[(cpt_points[0][0] + cpt_points[1][0]) / 2, (cpt_points[0][1] + cpt_points[1][1]) / 2, (cpt_points[0][2] + cpt_points[1][2]) / 2]]
# blh_points = cpt_to_blh(cpt_points, 121.2925645406, 30.2122941425, 30.206394, -0.427793, 1.288437, 202.06017)
# print(blh_points)
# _*_ coding: utf-8 _*_ #
# @Time: 2020/6/1 11:40
# @Author: XiongChao
from osgeo import ogr, osr
import math
def transform_to_utm(blh_dataset, source_srs):
lon = blh_dataset[0][0]
utm_zone = 31 + math.floor(lon / 6.0)
utm_srs = osr.SpatialReference()
utm_srs.SetUTM(utm_zone, 1)
src_to_utm_trans = osr.CreateCoordinateTransformation(source_srs, utm_srs)
utm_dataset = []
for blh_point in blh_dataset:
temp_point = blh_point
utm_point = list(src_to_utm_trans.TransformPoint(temp_point[0], temp_point[1]))
utm_point[2] = blh_point[2]
utm_dataset.append(utm_point)
return utm_dataset, utm_srs
def transform_to_src(utm_dataset, source_srs, utm_srs):
utm_to_src_trans = osr.CreateCoordinateTransformation(utm_srs, source_srs)
src_dataset = []
for utm_point in utm_dataset:
temp_point = utm_point
src_point = list(utm_to_src_trans.TransformPoint(temp_point[0], temp_point[1]))
src_point[2] = utm_point[2]
src_dataset.append(src_point)
return src_dataset
def create_line_geometry(dataset):
geo = ogr.Geometry(ogr.wkbLineStringZM)
for i in range(len(dataset)):
geo.SetPoint(i, dataset[i][0], dataset[i][1], dataset[i][2])
return geo
def create_point_geometry(point):
geo = ogr.Geometry(ogr.wkbPointZM)
geo.SetPoint(0, point[0], point[1], point[2])
return geo
# _*_ coding: utf-8 _*_ #
# @Time: 2020/1/7 19:37
# @Author: XiongChao
from osgeo import ogr, osr
import math
import copy
from util import const
from util.common import is_same_point, three_point_dot_product, two_point_distance
def get_buffer_point(point1, point2):
# 获取两个点,任意一个点与point1的连线垂直于point1与point2的连线,此点到point1的距离为const.RECTANGLE_WIDTH
result1 = const.RECTANGLE_WIDTH / two_point_distance(point1, point2)
x1 = point1[0] - result1 * (point2[1] - point1[1])
y1 = point1[1] + result1 * (point2[0] - point1[0])
x2 = point1[0] + result1 * (point2[1] - point1[1])
y2 = point1[1] - result1 * (point2[0] - point1[0])
z = (point1[2] + point2[2]) / 2
return (x1, y1, z), (x2, y2, z)
def get_buffer_points(point1, point2):
# 根据point1和point2构造buffer,并返回四个点
data1, data2 = get_buffer_point(point1, point2)
data3, data4 = get_buffer_point(point2, point1)
return [data1, data2, data3, data4]
def intersection_of_two_line(point1, point2, point3, point4):
# 获取两条直线的交点, point1和point2为一条线的起始点,point3和point4为一条直线的起始点
# 在这里,如果两条线平行,那么必然是数据有问题
result1 = (point2[1] - point1[1]) * (point4[0] - point3[0]) - (point4[1] - point3[1]) * (point2[0] - point1[0])
# if result1 == 0:
# return False
result2 = (point2[1] - point1[1]) * point1[0] - (point2[0] - point1[0]) * point1[1]
result3 = (point4[1] - point3[1]) * point3[0] - (point4[0] - point3[0]) * point3[1]
x = ((point4[0] - point3[0]) * result2 - (point2[0] - point1[0]) * result3) / result1
y = ((point4[1] - point3[1]) * result2 - (point2[1] - point1[1]) * result3) / result1
z = (point1[2] + point2[2]) / 2 # point3和point4为边界线,为BL
return x, y, z
def truncate_or_offset_line(dataset, point):
# dataset为数据集,point为数据集与边界的交点,截断数据集或者延长数据集
copy_dataset = copy.copy(dataset)
point1 = dataset.pop()
if is_same_point(point1, point):
point1 = dataset.pop()
while dataset:
point2 = dataset.pop()
if is_same_point(point2, point):
point2 = dataset.pop()
result = three_point_dot_product(point2, point1, point)
if result > 0:
point1 = point2
else:
dataset.append(point2)
dataset.append(point1)
break
if len(dataset) == 0:
dataset.append(copy_dataset[0])
return dataset
def create_line_geometry(dataset):
geo = ogr.Geometry(ogr.wkbLineStringZM)
for i in range(len(dataset)):
geo.SetPoint(i, dataset[i][0], dataset[i][1], dataset[i][2])
return geo
def create_point_geometry(point):
geo = ogr.Geometry(ogr.wkbPointZM)
geo.SetPoint(0, point[0], point[1], point[2])
return geo
def remove_duplicate_data(dataset):
# 将数据集中重复的点去掉,每一个feature至少有起点和终点,并且这两个点不同
# 重点肯定是连续的,并且数量不确定
dataset1 = copy.copy(dataset[::-1])
target_dataset = []
point1 = dataset1.pop()
target_dataset.append(point1)
while dataset1:
point2 = dataset1.pop()
if is_same_point(point1, point2):
continue
target_dataset.append(point2)
point1 = point2
return target_dataset
def transform_to_utm(blh_dataset, source_srs):
lon = blh_dataset[0][0]
utm_zone = 31 + math.floor(lon / 6.0)
utm_srs = osr.SpatialReference()
utm_srs.SetUTM(utm_zone, 1)
src_to_utm_trans = osr.CreateCoordinateTransformation(source_srs, utm_srs)
utm_dataset = []
for blh_point in blh_dataset:
temp_point = blh_point
utm_point = list(src_to_utm_trans.TransformPoint(temp_point[0], temp_point[1]))
utm_point[2] = blh_point[2]
utm_dataset.append(utm_point)
return utm_dataset, utm_srs
def transform_to_src(utm_dataset, source_srs, utm_srs):
utm_to_src_trans = osr.CreateCoordinateTransformation(utm_srs, source_srs)
src_dataset = []
for utm_point in utm_dataset:
temp_point = utm_point
src_point = list(utm_to_src_trans.TransformPoint(temp_point[0], temp_point[1]))
src_point[2] = utm_point[2]
src_dataset.append(src_point)
return src_dataset
def create_polygon_geometry(dataset):
ring = ogr.Geometry(ogr.wkbLinearRing)
for i in range(len(dataset)):
ring.AddPoint(dataset[i][0], dataset[i][1], dataset[i][2])
yard = ogr.Geometry(ogr.wkbPolygon)
yard.AddGeometry(ring)
yard.CloseRings()
return yard
......@@ -5,44 +5,226 @@
import sys
import os
from osgeo import osr
from osgeo import osr, ogr, gdal
import csv
from util import const
from connect_solution.connect_mesh import ConnectMeshByMeshList
from refresh_topo_next import RefreshTopoNext
sys.path.append('.')
sys.path.append("../")
from connect_solution import ConnectMeshs
gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES") # 支持中文路径
gdal.SetConfigOption("SHAPE_ENCODING", "") # 使属性表支持中文
ogr.RegisterAll() # 注册所有驱动
def get_mesh_list(sheet_designation_path, output_info1):
# 获取所有的图幅号列表
mesh_ids = []
with open(sheet_designation_path, 'r', encoding='utf-8') as file:
lines = file.readlines()
for line in lines:
new_line = line.strip()
if new_line and new_line.isdigit():
mesh_ids.append(int(new_line))
elif new_line and not new_line.isdigit():
# 无法解析的字符串
output_info1.append(new_line + "无法解析,格式有问题\n")
return mesh_ids
def write_info(output_document_path, output_info1, output_info2, output_info3):
# 将存储的接边信息写到指定的文件路径下
path1 = output_document_path + "\\未接边信息.csv"
path2 = output_document_path + "\\已接边信息.txt"
path3 = output_document_path + "\\不需要接边信息.txt"
with open(path1, 'w', encoding='utf_8_sig', newline='') as file1:
writer1 = csv.writer(file1)
writer1.writerow(["***** " + '-' * 47 + "报错/重要信息" + '-' * 47 + "*****"])
for info in output_info1:
writer1.writerow([str(info)])
with open(path2, 'w', encoding='utf-8') as file2:
for info in output_info2:
file2.write(info + '\n')
with open(path3, 'w', encoding='utf-8') as file3:
for info in output_info3:
file3.write(info + '\n')
def get_adjacent_mesh_box(mesh_id, box_layer, output_info1):
# 获取指定图幅号附近的8个图幅框的id
filter = '"' + const.MESH_ID + '" = ' + "'" + str(mesh_id) + "'"
box_layer.SetAttributeFilter(filter)
if int(box_layer.GetFeatureCount()) != 1:
output_info = "根据图幅号" + str(mesh_id) + "获取到的图幅框不是1个"
output_info1.append(output_info)
box_layer.SetAttributeFilter(None)
return []
mesh_box_feature = box_layer.GetNextFeature()
box_layer.SetAttributeFilter(None) # 消除属性过滤
mesh_box_geom = mesh_box_feature.GetGeometryRef()
mesh_box_buffer = mesh_box_geom.Buffer(const.BUFFER_SIZE1, 4)
mesh_box_buffer_points = mesh_box_buffer.GetGeometryRef(0).GetPoints()
lon_list = [point[0] for point in mesh_box_buffer_points]
lat_list = [point[1] for point in mesh_box_buffer_points]
min_lon, max_lon = min(lon_list), max(lon_list)
min_lat, max_lat = min(lat_list), max(lat_list)
box_layer.SetSpatialFilterRect(min_lon, min_lat, max_lon, max_lat)
mesh_box_count = box_layer.GetFeatureCount()
mesh_ids = []
for _ in range(mesh_box_count):
mesh_box_feature = box_layer.GetNextFeature()
if mesh_box_feature is None or mesh_box_feature[const.MESH_ID] == mesh_id:
continue
mesh_ids.append(mesh_box_feature[const.MESH_ID])
box_layer.SetSpatialFilter(None) # 消除空间过滤
box_layer.ResetReading()
return mesh_ids
def run(connect_postgresql, sheet_designation_path, mesh_box_path, output_document_path):
# 参数依次为pg库连接字符串、图幅号文件路径,图幅框文件路径、输出文档路径
output_info1, output_info2, output_info3 = [], [], []
output_info = "*****" + '-' * 13 + "有关topo图层/图幅框图层没有字段的异常信息,大概率是上游数据的命名不符合规范的问题" + \
'-' * 14 + "*****"
output_info1.append(output_info)
output_info = "*****对于数据错误修改数据后重新上传的数据," \
"请确认重新上传是否会对已经接边的数据产生影响,如果有影响,请重新选接边*****"
output_info1.append(output_info)
if connect_postgresql[:2] != 'PG':
output_info = "传入的数据库处理字符串不是PG库,请确认后重试!"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
hd_data_source = ogr.Open(connect_postgresql, 1)
if hd_data_source is None:
output_info = "根据连接字符串获取不到数据库,请确认后重试!"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
if os.path.exists(sheet_designation_path) is False:
output_info = "根据输入的图幅号列表文件路径,找不到指定的文件,请确认后重试!"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
if sheet_designation_path[-3:].lower() != 'txt':
output_info = "传入的图幅号列表文件的格式不是txt格式,运行结束"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
if os.path.exists(mesh_box_path) is False:
output_info = "根据输入的图幅框文件路径没有获取到图幅框文件,请确认后重试!"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
if mesh_box_path[-4:] != 'gpkg':
output_info = "传入的图幅框文件格式不是gpkg格式,请确认后重试"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
mesh_box_data_source = ogr.Open(mesh_box_path, 0)
if mesh_box_data_source is None:
output_info = "图幅框文件中没有任何数据,请确认后重试!"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
mesh_box_layer = mesh_box_data_source.GetLayerByName(const.MESH_BOX_LAYER_NAME)
if mesh_box_layer is None:
output_info = "从输入的图幅框文件中无法获取图幅框图层,请查验后重试!"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
if mesh_box_layer.FindFieldIndex(const.MESH_ID, False) == -1:
output_info = "图幅框图层没有图幅号字段或者图幅号字段的字段名不符合规格文件的命名,程序自动改名"
output_info2.append(output_info)
field_index = mesh_box_layer.FindFieldIndex("meshid", False)
if field_index == -1:
output_info = "图幅框的图幅号字段更改失败,请反馈给研发"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
else:
field_name = ogr.FieldDefn(const.MESH_ID, ogr.OFTString)
if mesh_box_layer.AlterFieldDefn(field_index, field_name, ogr.ALTER_NAME_FLAG) == ogr.OGRERR_NONE:
output_info2.append("图幅框的图幅号字段更改成功")
write_info(output_document_path, output_info1, output_info2, output_info3)
else:
output_info1.append("图幅框的图幅号字段更改失败")
write_info(output_document_path, output_info1, output_info2, output_info3)
if os.path.exists(output_document_path) is False:
output_info = "输入接边信息文档的路径不是有效的路径,请确认后重试"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
mesh_ids = get_mesh_list(sheet_designation_path, output_info1)
if len(mesh_ids) < 1:
output_info = "传入的图幅号列表文件中没有图幅号,请查验后重试!"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
try:
for mesh_id in mesh_ids:
new_mesh_ids = get_adjacent_mesh_box(mesh_id, mesh_box_layer, output_info1)
if not new_mesh_ids:
write_info(output_document_path, output_info1, output_info2, output_info3)
return
for new_mesh_id in new_mesh_ids:
if not RefreshTopoNext().run(hd_data_source, new_mesh_id):
output_info = "调用上游程序,未能成功刷新图幅边界上topo点的前驱后继关系"
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
return
print("调用上游程序修改topo点成功")
except Exception as e:
output_info = "调用上游刷新topo程序时,出现异常,异常为:" + str(e)
output_info1.append(output_info)
write_info(output_document_path, output_info1, output_info2, output_info3)
if hd_data_source:
hd_data_source.Destroy()
if mesh_box_data_source:
mesh_box_data_source.Destroy()
return
def run(connect_postgresql, sheet_designation_path, mesh_box_path):
# 参数依次为pg库连接字符串、图幅号文件路径,图幅框文件路径
solution = ConnectMeshs(connect_postgresql, sheet_designation_path, mesh_box_path)
try:
solution.models()
print("接边成功,请确认!")
write_info(output_document_path, output_info1, output_info2, output_info3)
ConnectMeshByMeshList(hd_data_source, mesh_box_layer, mesh_ids, output_document_path).main()
print("接边已全部运行完毕,请确认!")
except Exception as e:
solution.mesh_box_data_source.Destroy()
solution.map_data_source.Destroy()
print(e)
print("接边程序出发异常,异常为:" + str(e))
finally:
if hd_data_source:
hd_data_source.Destroy()
if mesh_box_data_source:
mesh_box_data_source.Destroy()
if __name__ == '__main__':
# arg1 = "PG:dbname='test3' host='localhost' port='5432' user='postgres' password='19931018'"
# arg2 = r"C:\Users\熊超\Desktop\edge_match_file\mesh_id.txt"
# arg3 = r"C:\Users\熊超\Desktop\edge_match_file\mesh_grid.gpkg"
# run(arg1, arg2, arg3)
arg1 = sys.argv[1] # 数据库连接字符串
arg2 = sys.argv[2] # 图幅号的txt文件
arg3 = sys.argv[3] # 图幅框文件
if arg1[:2].lower() != "pg":
print("请传入连接pg库字符串!")
else:
if arg2[-3:].lower() != "txt":
print("请传入存储图幅号的txt文件")
else:
if arg3[-4:].lower() != "gpkg":
print("请传入图幅框的gpkg文件!")
else:
current_path = os.path.dirname(os.path.abspath(__file__))
gdal_data = current_path + '\\proj'
osr.SetPROJSearchPath(gdal_data)
run(arg1, arg2, arg3)
arg1 = r"PG:dbname='test2' host='localhost' port='5432' user='postgres' password='19931018'"
arg2 = r"C:\Users\熊超\Desktop\edge_match_file\mesh_ids.txt"
arg3 = r"C:\Users\熊超\Desktop\edge_match_file\mesh_grid.gpkg"
arg4 = r"C:\Users\熊超\Desktop\edge_match_file"
run(arg1, arg2, arg3, arg4)
# arg1, arg2, arg3, arg4 = None, None, None, None
# try:
# arg1 = sys.argv[1] # 数据库连接字符串
# arg2 = sys.argv[2] # 图幅号的txt文件
# arg3 = sys.argv[3] # 图幅框文件
# arg4 = sys.argv[4] # 接边报表路径
# current_path = os.path.dirname(os.path.abspath(__file__))
# gdal_data = current_path + '\\proj'
# osr.SetPROJSearchPath(gdal_data)
# except Exception as e:
# print("传入的参数有问题,程序发生异常,异常为:" + str(e))
# run(arg1, arg2, arg3, arg4)
# _*_ coding: utf-8 _*_ #
# @Time: 2020/5/28 10:45
# @Author: XiongChao
\ No newline at end of file
......@@ -7,14 +7,14 @@ import numpy as np
import math
def two_point_distance(point1, point2):
def two_point_distance_2d(point1, point2):
# 计算二元坐标的距离
return math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2)
def interpolation_between_two_point(start_point, end_point, ratio):
def interpolation_between_two_point_2d(start_point, end_point, ratio):
# 在起点与终点之间进行插值,ratio是插值的数量
distance = two_point_distance(start_point, end_point)
distance = two_point_distance_2d(start_point, end_point)
n = int(distance * ratio) # 插值的个数
dataset = []
result = np.linspace(0, 1, n + 2)[:n + 1] # 保留首,不保留尾
......@@ -23,18 +23,45 @@ def interpolation_between_two_point(start_point, end_point, ratio):
y = k * (end_point[1] - start_point[1]) + start_point[1]
if len(start_point) == 3:
z = k * (end_point[2] - start_point[2]) + start_point[2]
point = (x, y, z)
point = [x, y, z]
else:
point = (x, y)
point = [x, y]
dataset.append(point)
return dataset
def interpolation_dataset(dataset, interpolation_mount):
def interpolation_dataset_2d(dataset, interpolation_mount):
m = len(dataset)
new_dataset = []
for i in range(m - 1):
new_dataset += interpolation_between_two_point(dataset[i], dataset[i + 1], interpolation_mount)
new_dataset += interpolation_between_two_point_3d(dataset[i], dataset[i + 1], interpolation_mount)
new_dataset.append(dataset[m - 1])
return new_dataset
def two_point_distance_3d(point1, point2):
# 计算二元坐标的距离
return math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2 + (point2[2] - point1[2]) ** 2)
def interpolation_between_two_point_3d(start_point, end_point, ratio):
# 在起点与终点之间进行插值,ratio是插值的数量
distance = two_point_distance_3d(start_point, end_point)
n = int(distance * ratio) # 插值的个数
dataset = []
result = np.linspace(0, 1, n + 2)[:n + 1] # 保留首,不保留尾
for k in result:
x = k * (end_point[0] - start_point[0]) + start_point[0]
y = k * (end_point[1] - start_point[1]) + start_point[1]
z = k * (end_point[2] - start_point[2]) + start_point[2]
dataset.append([x, y, z])
return dataset
def interpolation_dataset_3d(dataset, interpolation_mount):
m = len(dataset)
new_dataset = []
for i in range(m - 1):
new_dataset += interpolation_between_two_point_3d(dataset[i], dataset[i + 1], interpolation_mount)
new_dataset.append(dataset[m - 1])
return new_dataset
# _*_ coding: utf-8 _*_ #
# @Time: 2020/5/27 15:27
# @Author: XiongChao
import numpy as np
from sklearn.neighbors import KDTree
import math
def two_point_distance_2d(point1, point2):
# 计算二维点的距离
return math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2)
def two_point_distance_3d(point1, point2):
# 计算三元坐标点的距离
return math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2 + (point2[2] - point1[2]) ** 2)
def interpolation_between_two_point(start_point, end_point, ratio):
# 在起点与终点之间进行插值,ratio是插值的数量
distance = two_point_distance_3d(start_point, end_point)
n = int(distance * ratio) # 插值的个数
dataset = []
result = np.linspace(0, 1, n + 2)[:n + 1] # 保留首,不保留尾
for k in result:
x = k * (end_point[0] - start_point[0]) + start_point[0]
y = k * (end_point[1] - start_point[1]) + start_point[1]
if len(start_point) == 3:
z = k * (end_point[2] - start_point[2]) + start_point[2]
point = [x, y, z]
else:
point = [x, y]
dataset.append(point)
return dataset
class CalElevation:
"""
使用局部加权计算新的插值点的高程值
通过高斯函数将距离映射为权重,带宽取距离之和除以一个常数
"""
def __init__(self, m, n):
self.m = m # 根据目标点附近的几个点计算目标点的高程
self.n = n # 插值反算的时候对原始数据的插值数;越密,高程计算越准确
@staticmethod
def get_epsilon(points, point):
# 获取带宽,带宽为所有点的距离之和
square_epsilon = 0
for point1 in points:
square_epsilon += two_point_distance_2d(point, point1) ** 2
return np.sqrt(square_epsilon)
@staticmethod
def weight_func(point1, point2, epsilon=0.1):
distance = two_point_distance_2d(point1, point2)
return np.exp(-distance ** 2 / (2 * epsilon ** 2))
def get_weighted_list(self, points, point):
epsilon = self.get_epsilon(points, point) / 6
weight_list = []
for point1 in points:
weight_list.append(self.weight_func(point, point1, epsilon))
return weight_list
def get_elevation(self, ori_points, new_points):
# new_points是需要计算高程的点集,ori_points是原始点集
new_dataset = interpolation_dataset(ori_points, self.n)
ori_xy_list = [[point[0], point[1]] for point in new_dataset]
tree = KDTree(np.array(ori_xy_list), leaf_size=20) # 二维kd数
for i in range(1, len(new_points) - 1):
point = new_points[i]
nearest_points = []
dist, ind = tree.query([np.array(point[:2])], k=self.m)
index_list = list(ind[0])
for index in index_list:
nearest_points.append(new_dataset[index])
elevation_list = [point[2] for point in nearest_points]
weight_list = self.get_weighted_list(nearest_points, point)
ratio_list = [weight / sum(weight_list) for weight in weight_list]
elevation = sum(ratio_list[i] * elevation_list[i] for i in range(len(ratio_list)))
new_points[i][2] = elevation
return new_points
def interpolation_dataset(dataset, interpolation_mount):
m = len(dataset)
new_dataset = []
for i in range(m - 1):
new_dataset += interpolation_between_two_point(dataset[i], dataset[i + 1], interpolation_mount)
new_dataset.append(dataset[m - 1])
return new_dataset
class ImprovedDouglasPeucker(object):
"""
1. 抽稀后得结果不要两个点两个点得形式
2. 第二考虑高程得问题
3. 二维点的抽稀,三维坐标是utm坐标系
4. 二维点抽稀完后按照指定长度进行插值,插值点的第三维坐标需要反算
"""
def __init__(self, interpolation_count=0.125):
self.threshold = 0.005
self.m = 4 # 插值反算高程的时候使用多少个点进行计算
self.n = 4 # 插值反算的时候对原始数据的插值数;越密,高程计算越准确
self.interpolation_count = interpolation_count # 抽稀后数据的插值密度
self.returned_list = []
self.not_dilute_list = []
@staticmethod
def construct_vector_by_point(point):
# 构造从point1指向point2的向量,返回的是列向量
return np.array([point[0], point[1]]).reshape(2, 1)
def get_distance_from_point_to_line(self, point1, point2, point3):
# point2, point3确定一条空间直线,point1到此直线的距离
tmp_var = np.squeeze(np.dot(
(self.construct_vector_by_point(point3) - self.construct_vector_by_point(point2)).T,
(self.construct_vector_by_point(point2) - self.construct_vector_by_point(point1)))) / \
np.linalg.norm(self.construct_vector_by_point(point3) - self.construct_vector_by_point(point2)) ** 2
vertical_point = -tmp_var * (self.construct_vector_by_point(point3) - self.construct_vector_by_point(point2)) \
+ self.construct_vector_by_point(point2)
return np.linalg.norm(self.construct_vector_by_point(point1) - self.construct_vector_by_point(vertical_point))
def dilute(self, points):
m = len(points)
if m < 3:
self.returned_list.extend(points[::-1])
return
max_distance, index = 0, 0
for i in range(1, m - 1):
distance = self.get_distance_from_point_to_line(points[i], points[0], points[-1])
if distance > max_distance:
max_distance = distance
index = i
if max_distance < self.threshold:
self.returned_list.append(points[-1])
self.returned_list.append(points[0])
else:
sequence1 = points[:index + 1]
sequence2 = points[index:]
sequences = [sequence1, sequence2]
for i in range(2):
sequence = sequences[i]
if len(sequence) < 3 and i == 1:
self.returned_list.extend(sequence[::-1])
else:
self.not_dilute_list.append(sequence)
@staticmethod
def is_same_point(point1, point2):
# 判断两个二维点是不是一个点
flag = np.array([np.abs(point1[i] - point2[i]) < 0.000001 for i in range(len(point1))])
if flag.all():
return True
return False
def remove_duplicated_point(self, points):
# 去除二维重点,重点是连续的
dataset1 = points[::-1] + []
target_dataset = []
point1 = dataset1.pop()
target_dataset.append(point1)
while dataset1:
point2 = dataset1.pop()
if self.is_same_point(point1[:2], point2[:2]):
continue
target_dataset.append(point2)
point1 = point2
return target_dataset
def main(self, points):
# points是三维数据
self.dilute(points)
while self.not_dilute_list:
self.dilute(self.not_dilute_list.pop())
self.returned_list = self.returned_list[::-1] # 在抽稀的过程中反向了
tmp_points = self.remove_duplicated_point(self.returned_list)
new_points = interpolation_dataset(tmp_points, self.interpolation_count)
return CalElevation(self.m, self.n).get_elevation(points, new_points)
# _*_ coding: utf-8 _*_ #
# @Time: 2020/6/1 14:42
# @Author: XiongChao
import numpy as np
class PosNegTrans:
"""
对给定的数据,将坐标系原点平移到数据质心,按照首点到尾点的方向设置x轴
这个类中包含建立局部坐标系时候质心和旋转角的计算方法,以及正向变换和反向变换的方法
"""
def __init__(self):
pass
@staticmethod
def cos_(vector1, vector2):
# 计算两个向量的余弦值
cos_theta = (vector1[0] * vector2[0] + vector1[1] * vector2[1]) / \
(np.sqrt(vector1[0] ** 2 + vector1[1] ** 2) * np.sqrt(vector2[0] ** 2 + vector2[1] ** 2))
# 由于浮点运算的截断误差,可能导致求得的值大于1或者小于-1,因此需要进行处理
if cos_theta >= 1:
cos_theta = 1
elif cos_theta <= -1:
cos_theta = -1
return cos_theta
def angle_of_two_vector(self, vector1, vector2):
# 返回两个向量的角度,返回的值为弧度
cos_theta = self.cos_(vector1, vector2)
theta = np.arccos(cos_theta)
return theta
@staticmethod
def intersection_of_two_line(point1, point2, point3, point4):
# 获取两条直线的交点, point1和point2为一条线的起始点,point3和point4为一条直线的起始点
# 在这里,如果两条线平行,那么必然是数据有问题
result1 = (point2[1] - point1[1]) * (point4[0] - point3[0]) - (point4[1] - point3[1]) * (point2[0] - point1[0])
if result1 == 0:
return []
result2 = (point2[1] - point1[1]) * point1[0] - (point2[0] - point1[0]) * point1[1]
result3 = (point4[1] - point3[1]) * point3[0] - (point4[0] - point3[0]) * point3[1]
x = ((point4[0] - point3[0]) * result2 - (point2[0] - point1[0]) * result3) / result1
y = ((point4[1] - point3[1]) * result2 - (point2[1] - point1[1]) * result3) / result1
return x, y
@staticmethod
def clockwise_counterclockwise(point1, point2, point3):
# 判断123是顺时针还是逆时针
# 计算向量point1point2与point2point3的向量积
result = (point2[0] - point1[0]) * (point3[1] - point2[1]) - (point2[1] - point1[1]) * (point3[0] - point2[0])
if result > 0:
# point1point2point3逆时针
return 1
else:
return 0
def rotation_angle(self, point1, point2):
# 计算坐标轴旋转的角度
vector1 = (point2[0] - point1[0], point2[1] - point1[1])
vector2 = (1, 0)
angle = self.angle_of_two_vector(vector1, vector2)
intersection = self.intersection_of_two_line(point1, point2, (0, 0), (1, 0))
if not intersection:
return angle
flag_point = [intersection[0] + vector1[0], intersection[1] + vector1[1]]
result = self.clockwise_counterclockwise((0, 0), (1, 0), flag_point)
if result == 0:
angle = 2 * np.pi - angle
return angle
@staticmethod
def data_normalization(dataset):
# 数据归一化,返回新数据和均值
x_dataset = [data[0] for data in dataset]
y_dataset = [data[1] for data in dataset]
x_mean = sum(x_dataset) / len(x_dataset)
y_mean = sum(y_dataset) / len(y_dataset)
new_dataset = [[data[0] - x_mean, data[1] - y_mean] for data in dataset]
return new_dataset, x_mean, y_mean
@staticmethod
def construct_positive_transformation_matrix(theta):
# theta为弧度值
positive_matrix = np.zeros((2, 2))
positive_matrix[0, 0] = np.cos(theta)
positive_matrix[0, 1] = np.sin(theta)
positive_matrix[1, 0] = -np.sin(theta)
positive_matrix[1, 1] = np.cos(theta)
return positive_matrix
def positive_transformation_point(self, theta, point):
# 正向变化一个点
coefficient_matrix = np.zeros((2, 1))
coefficient_matrix[0, 0], coefficient_matrix[1, 0] = point[0], point[1]
transformation_matrix = self.construct_positive_transformation_matrix(theta)
positive_matrix = np.dot(transformation_matrix, coefficient_matrix)
positive_point = [positive_matrix[0, 0], positive_matrix[1, 0]]
return positive_point
@staticmethod
def construct_negative_transformation_matrix(theta):
# theta为弧度值
negative_matrix = np.zeros((2, 2))
negative_matrix[0, 0] = np.cos(theta)
negative_matrix[0, 1] = - np.sin(theta)
negative_matrix[1, 0] = np.sin(theta)
negative_matrix[1, 1] = np.cos(theta)
return negative_matrix
def negative_transformation_point(self, theta, point):
# 反向变换一个点
coefficient_matrix = np.zeros((2, 1))
coefficient_matrix[0, 0], coefficient_matrix[1, 0] = point[0], point[1]
transformation_matrix = self.construct_negative_transformation_matrix(theta)
negative_matrix = np.dot(transformation_matrix, coefficient_matrix)
negative_point = [negative_matrix[0, 0], negative_matrix[1, 0]]
return negative_point
def positive_transformation(self, dataset):
# 对数据集(点集)进行正向旋转平移变换,使用起点和终点构建方向
new_dataset, x_mean, y_mean = self.data_normalization(dataset)
n = len(dataset)
coefficient_matrix = np.zeros((2, n))
for i in range(n):
coefficient_matrix[:, i] = new_dataset[i]
theta = self.rotation_angle(new_dataset[0], new_dataset[-1])
transformation_matrix = self.construct_positive_transformation_matrix(theta)
positive_matrix = np.dot(transformation_matrix, coefficient_matrix)
m = len(positive_matrix[0])
positive_dataset = [[]] * m
for i in range(m):
positive_dataset[i] = list(positive_matrix[:, i])
return positive_dataset, theta, x_mean, y_mean
def negative_transformation(self, dataset, theta, x_mean, y_mean):
# 对数据集(点集)进行反向旋转平移变换,使用起点和终点构建方向
n = len(dataset)
coefficient_matrix = np.zeros((2, n))
for i in range(n):
coefficient_matrix[: i] = dataset[i]
transformation_matrix = self.construct_negative_transformation_matrix(theta)
mean_matrix = np.zeros((2, 1))
mean_matrix[0, 0] = x_mean
mean_matrix[1, 0] = y_mean
negative_matrix = np.dot(transformation_matrix, coefficient_matrix) + mean_matrix
m = len(coefficient_matrix[0])
negative_dataset = [[]] * m
for i in range(m):
negative_dataset[i] = list(negative_matrix[:, i])
return negative_dataset
class PolyFit:
"""
多项式拟合
使用方式:
1. 指定次数拟合:
调用poly_fitting_parameter,给定数据集和次数即可,返回结果需要判断是否系数矩阵列满秩
2. 不指定次数,但指定次数列表:
调用get_degree_of_polynomial,给定数据集和次数列表,返回损失最小对应的次数和参数,返回结果需要判断是否系数矩阵列满秩
"""
def __init__(self):
pass
@staticmethod
def get_coefficient_vector(point, n):
# 根据一个点,返回一个(n + 1) × 1的矩阵
vector = np.zeros(n + 1)
for i in range(n + 1):
vector[i] = np.power(point[0], i)
return vector.reshape(n + 1, -1) # 返回的对象是二维
@staticmethod
def get_coefficient_matrix(dataset, n):
# 获取系数矩阵,为m × (n + 1)
m = len(dataset)
coefficient_matrix = np.zeros((m, n + 1))
for i in range(m):
for j in range(n + 1):
coefficient_matrix[i, j] = np.power(dataset[i][0], j)
return coefficient_matrix
@staticmethod
def get_standard_y_matrix(dataset):
# 获取原始的Y值矩阵,为m × 1
m = len(dataset)
Y_matrix = np.zeros((m, 1))
for i in range(m):
Y_matrix[i, 0] = dataset[i][1]
return Y_matrix
def poly_fitting_parameter(self, dataset, n):
# dataset中有m个点用于拟合,拟合的多项式的次数为n次
coefficient_matrix = self.get_coefficient_matrix(dataset, n)
Y_matrix = self.get_standard_y_matrix(dataset)
tmp_matrix = np.dot(coefficient_matrix.T, coefficient_matrix)
if np.linalg.det(tmp_matrix) == 0:
# 返回的最好是同一种数据结构,便于判断,比如Numpy数组没有 not array判断
return np.zeros(n)
result = np.linalg.inv(tmp_matrix)
parameter = np.dot(np.dot(result, coefficient_matrix.T), Y_matrix) # 为(n + 1) × 1矩阵
return parameter
def fitting_loss(self, dataset, n):
# 指定次数的多项式拟合损失
parameter = self.poly_fitting_parameter(dataset, n)
if parameter.shape == (n,):
# 出现奇异矩阵的时候,默认损失为无穷
return np.inf, []
loss = 0
for data in dataset:
X_vector = self.get_coefficient_vector(data, n)
Y_predict = np.dot(X_vector.T, parameter)
loss += np.squeeze(data[1] - Y_predict) ** 2
return loss, parameter
def get_degree_of_polynomial(self, dataset, degree_list=(1, 2)):
# 返回损失值最小的多项式次数
# 默认仅遍历到2次
loss_list = []
parameter_list = []
for n in degree_list:
loss, parameter = self.fitting_loss(dataset, n)
loss_list.append(loss)
parameter_list.append(parameter)
result = np.array([value == np.inf for value in loss_list])
if result.all():
# 遍历的次数下,构造的系数矩阵都为奇异矩阵
return -1, []
degree = loss_list.index(min(loss_list))
optimization_parameter = parameter_list[degree]
return degree + 1, optimization_parameter
@staticmethod
def data_normalization(dataset):
# 数据归一化,返回新数据和均值
x_dataset = [data[0] for data in dataset]
y_dataset = [data[1] for data in dataset]
x_mean = sum(x_dataset) / len(x_dataset)
y_mean = sum(y_dataset) / len(y_dataset)
new_dataset = [[data[0] - x_mean, data[1] - y_mean] for data in dataset]
return new_dataset, x_mean, y_mean
# _*_ coding: utf-8 _*_ #
# @Time: 2020/5/28 16:57
# @Author: XiongChao
import numpy as np
DEF_WINDOW_SIZE = 7 # 滑动窗口拟合时候的窗口大小
class PosNegTrans:
"""
对给定的数据,将坐标系原点平移到数据质心,按照首点到尾点的方向设置x轴
这个类中包含建立局部坐标系时候质心和旋转角的计算方法,以及正向变换和反向变换的方法
"""
def __init__(self):
pass
@staticmethod
def cos_(vector1, vector2):
# 计算两个向量的余弦值
cos_theta = (vector1[0] * vector2[0] + vector1[1] * vector2[1]) / \
(np.sqrt(vector1[0] ** 2 + vector1[1] ** 2) * np.sqrt(vector2[0] ** 2 + vector2[1] ** 2))
# 由于浮点运算的截断误差,可能导致求得的值大于1或者小于-1,因此需要进行处理
if cos_theta >= 1:
cos_theta = 1
elif cos_theta <= -1:
cos_theta = -1
return cos_theta
def angle_of_two_vector(self, vector1, vector2):
# 返回两个向量的角度,返回的值为弧度
cos_theta = self.cos_(vector1, vector2)
theta = np.arccos(cos_theta)
return theta
@staticmethod
def intersection_of_two_line(point1, point2, point3, point4):
# 获取两条直线的交点, point1和point2为一条线的起始点,point3和point4为一条直线的起始点
# 在这里,如果两条线平行,那么必然是数据有问题
result1 = (point2[1] - point1[1]) * (point4[0] - point3[0]) - (point4[1] - point3[1]) * (point2[0] - point1[0])
if result1 == 0:
return []
result2 = (point2[1] - point1[1]) * point1[0] - (point2[0] - point1[0]) * point1[1]
result3 = (point4[1] - point3[1]) * point3[0] - (point4[0] - point3[0]) * point3[1]
x = ((point4[0] - point3[0]) * result2 - (point2[0] - point1[0]) * result3) / result1
y = ((point4[1] - point3[1]) * result2 - (point2[1] - point1[1]) * result3) / result1
return x, y
@staticmethod
def clockwise_counterclockwise(point1, point2, point3):
# 判断123是顺时针还是逆时针
# 计算向量point1point2与point2point3的向量积
result = (point2[0] - point1[0]) * (point3[1] - point2[1]) - (point2[1] - point1[1]) * (point3[0] - point2[0])
if result > 0:
# point1point2point3逆时针
return 1
else:
return 0
def rotation_angle(self, point1, point2):
# 计算坐标轴旋转的角度
vector1 = (point2[0] - point1[0], point2[1] - point1[1])
vector2 = (1, 0)
angle = self.angle_of_two_vector(vector1, vector2)
intersection = self.intersection_of_two_line(point1, point2, (0, 0), (1, 0))
if not intersection:
return angle
flag_point = [intersection[0] + vector1[0], intersection[1] + vector1[1]]
result = self.clockwise_counterclockwise((0, 0), (1, 0), flag_point)
if result == 0:
angle = 2 * np.pi - angle
return angle
@staticmethod
def data_normalization(dataset):
# 数据归一化,返回新数据和均值
x_dataset = [data[0] for data in dataset]
y_dataset = [data[1] for data in dataset]
x_mean = sum(x_dataset) / len(x_dataset)
y_mean = sum(y_dataset) / len(y_dataset)
new_dataset = [[data[0] - x_mean, data[1] - y_mean] for data in dataset]
return new_dataset, x_mean, y_mean
@staticmethod
def construct_positive_transformation_matrix(theta):
# theta为弧度值
positive_matrix = np.zeros((2, 2))
positive_matrix[0, 0] = np.cos(theta)
positive_matrix[0, 1] = np.sin(theta)
positive_matrix[1, 0] = -np.sin(theta)
positive_matrix[1, 1] = np.cos(theta)
return positive_matrix
def positive_transformation_point(self, theta, point):
# 正向变化一个点
coefficient_matrix = np.zeros((2, 1))
coefficient_matrix[0, 0], coefficient_matrix[1, 0] = point[0], point[1]
transformation_matrix = self.construct_positive_transformation_matrix(theta)
positive_matrix = np.dot(transformation_matrix, coefficient_matrix)
positive_point = [positive_matrix[0, 0], positive_matrix[1, 0]]
return positive_point
@staticmethod
def construct_negative_transformation_matrix(theta):
# theta为弧度值
negative_matrix = np.zeros((2, 2))
negative_matrix[0, 0] = np.cos(theta)
negative_matrix[0, 1] = - np.sin(theta)
negative_matrix[1, 0] = np.sin(theta)
negative_matrix[1, 1] = np.cos(theta)
return negative_matrix
def negative_transformation_point(self, theta, point):
# 反向变换一个点
coefficient_matrix = np.zeros((2, 1))
coefficient_matrix[0, 0], coefficient_matrix[1, 0] = point[0], point[1]
transformation_matrix = self.construct_negative_transformation_matrix(theta)
negative_matrix = np.dot(transformation_matrix, coefficient_matrix)
negative_point = [negative_matrix[0, 0], negative_matrix[1, 0]]
return negative_point
def positive_transformation(self, dataset):
# 对数据集(点集)进行正向旋转平移变换,使用起点和终点构建方向
new_dataset, x_mean, y_mean = self.data_normalization(dataset)
n = len(dataset)
coefficient_matrix = np.zeros((2, n))
for i in range(n):
coefficient_matrix[:, i] = new_dataset[i]
theta = self.rotation_angle(new_dataset[0], new_dataset[-1])
transformation_matrix = self.construct_positive_transformation_matrix(theta)
positive_matrix = np.dot(transformation_matrix, coefficient_matrix)
m = len(positive_matrix[0])
positive_dataset = [[]] * m
for i in range(m):
positive_dataset[i] = list(positive_matrix[:, i])
return positive_dataset, theta, x_mean, y_mean
def negative_transformation(self, dataset, theta, x_mean, y_mean):
# 对数据集(点集)进行反向旋转平移变换,使用起点和终点构建方向
n = len(dataset)
coefficient_matrix = np.zeros((2, n))
for i in range(n):
coefficient_matrix[: i] = dataset[i]
transformation_matrix = self.construct_negative_transformation_matrix(theta)
mean_matrix = np.zeros((2, 1))
mean_matrix[0, 0] = x_mean
mean_matrix[1, 0] = y_mean
negative_matrix = np.dot(transformation_matrix, coefficient_matrix) + mean_matrix
m = len(coefficient_matrix[0])
negative_dataset = [[]] * m
for i in range(m):
negative_dataset[i] = list(negative_matrix[:, i])
return negative_dataset
class PolyFit:
"""
多项式拟合
使用方式:
1. 指定次数拟合:
调用poly_fitting_parameter,给定数据集和次数即可,返回结果需要判断是否系数矩阵列满秩
2. 不指定次数,但指定次数列表:
调用get_degree_of_polynomial,给定数据集和次数列表,返回损失最小对应的次数和参数,返回结果需要判断是否系数矩阵列满秩
"""
def __init__(self):
pass
@staticmethod
def get_coefficient_vector(point, n):
# 根据一个点,返回一个(n + 1) × 1的矩阵
vector = np.zeros(n + 1)
for i in range(n + 1):
vector[i] = np.power(point[0], i)
return vector.reshape(n + 1, -1) # 返回的对象是二维
@staticmethod
def get_coefficient_matrix(dataset, n):
# 获取系数矩阵,为m × (n + 1)
m = len(dataset)
coefficient_matrix = np.zeros((m, n + 1))
for i in range(m):
for j in range(n + 1):
coefficient_matrix[i, j] = np.power(dataset[i][0], j)
return coefficient_matrix
@staticmethod
def get_standard_y_matrix(dataset):
# 获取原始的Y值矩阵,为m × 1
m = len(dataset)
Y_matrix = np.zeros((m, 1))
for i in range(m):
Y_matrix[i, 0] = dataset[i][1]
return Y_matrix
def poly_fitting_parameter(self, dataset, n):
# dataset中有m个点用于拟合,拟合的多项式的次数为n次
coefficient_matrix = self.get_coefficient_matrix(dataset, n)
Y_matrix = self.get_standard_y_matrix(dataset)
tmp_matrix = np.dot(coefficient_matrix.T, coefficient_matrix)
if np.linalg.det(tmp_matrix) == 0:
# 返回的最好是同一种数据结构,便于判断,比如Numpy数组没有 not array判断
return []
result = np.linalg.inv(tmp_matrix)
parameter = np.dot(np.dot(result, coefficient_matrix.T), Y_matrix) # 为(n + 1) × 1矩阵
return parameter
def fitting_loss(self, dataset, n):
# 指定次数的多项式拟合损失
parameter = self.poly_fitting_parameter(dataset, n)
if parameter.shape == (n,):
# 出现奇异矩阵的时候,默认损失为无穷
return np.inf, []
loss = 0
for data in dataset:
X_vector = self.get_coefficient_vector(data, n)
Y_predict = np.dot(X_vector.T, parameter)
loss += np.squeeze(data[1] - Y_predict) ** 2
return loss, parameter
def get_degree_of_polynomial(self, dataset, degree_list=(1, 2)):
# 返回损失值最小的多项式次数
# 默认仅遍历到2次
loss_list = []
parameter_list = []
for n in degree_list:
loss, parameter = self.fitting_loss(dataset, n)
loss_list.append(loss)
parameter_list.append(parameter)
result = np.array([value == np.inf for value in loss_list])
if result.all():
# 遍历的次数下,构造的系数矩阵都为奇异矩阵
return -1, []
degree = loss_list.index(min(loss_list))
optimization_parameter = parameter_list[degree]
return degree + 1, optimization_parameter
@staticmethod
def data_normalization(dataset):
# 数据归一化,返回新数据和均值
x_dataset = [data[0] for data in dataset]
y_dataset = [data[1] for data in dataset]
x_mean = sum(x_dataset) / len(x_dataset)
y_mean = sum(y_dataset) / len(y_dataset)
new_dataset = [[data[0] - x_mean, data[1] - y_mean] for data in dataset]
return new_dataset, x_mean, y_mean
class SavitzkyGolayFilter:
"""
SG_Filter:输入数据是utm坐标系,这样能够保证z值不会在平滑前后发生改变
1.先对数据进行插值,防止滤波后失真
2. 对插值数据进行滑动窗口拟合,每次拟合调用局部坐标系接口,防止拟合误差较大或者系数矩阵列不满秩
3. 对滤波后的数据进行抽稀和插值
"""
def __init__(self, interpolation_count=0.125):
self.trans = PosNegTrans() # 坐标系转移类
self.poly_fit = PolyFit() # 多项式拟合类
def savgol(self, dataset, window_size=DEF_WINDOW_SIZE):
m = len(dataset)
if m <= window_size:
return dataset
smoothing_dataset = [[]] * m # 存储最终的平滑数据
smoothing_dataset[0], smoothing_dataset[-1] = dataset[0], dataset[-1] # 保留首尾点
# 对开始几个点的平滑
positive_dataset, theta, x_mean, y_mean = self.trans.positive_transformation(dataset[:window_size])
degree, parameter = self.poly_fit.get_degree_of_polynomial(positive_dataset, (1, 2))
if degree != -1:
for i in range(1, window_size // 2):
point = dataset[i]
new_point = [point[0] - x_mean, point[1] - y_mean]
positive_point = self.trans.positive_transformation_point(theta, new_point)
X_vector = self.poly_fit.get_coefficient_vector(positive_point, degree)
Y_predict = float(np.squeeze(np.dot(X_vector.T, parameter))) # 去掉矩阵的维度,变为一个浮点数
negative_point = np.zeros((2, 1))
negative_point[0, 0], negative_point[1, 0] = positive_point[0], Y_predict
result_point = self.trans.negative_transformation_point(theta, negative_point)
target_point = [result_point[0] + x_mean, result_point[1] + y_mean]
smoothing_dataset[i] = target_point
# 对最后几个点的平滑
positive_dataset, theta, x_mean, y_mean = self.trans.positive_transformation(dataset[-window_size:])
degree, parameter = self.poly_fit.get_degree_of_polynomial(positive_dataset, (1, 2))
if degree != -1:
for j in range(m - (window_size - 1) // 2, m - 1):
point = dataset[j]
new_point = [point[0] - x_mean, point[1] - y_mean]
positive_point = self.trans.positive_transformation_point(theta, new_point)
X_vector = self.poly_fit.get_coefficient_vector(positive_point, degree)
Y_predict = float(np.squeeze(np.dot(X_vector.T, parameter))) # 去掉矩阵的维度,变为一个浮点数
negative_point = np.zeros((2, 1))
negative_point[0, 0], negative_point[1, 0] = positive_point[0], Y_predict
result_point = self.trans.negative_transformation_point(theta, negative_point)
target_point = [result_point[0] + x_mean, result_point[1] + y_mean]
smoothing_dataset[j] = target_point
# 获取中间点的平滑值
for i in range(window_size // 2, m - (window_size - 1) // 2):
data = dataset[i - window_size // 2: i + window_size // 2 + 1]
positive_dataset, theta, x_mean, y_mean = self.trans.positive_transformation(data)
degree, parameter = self.poly_fit.get_degree_of_polynomial(positive_dataset, (1, 2))
if degree != -1:
point = dataset[i]
new_point = [point[0] - x_mean, point[1] - y_mean]
positive_point = self.trans.positive_transformation_point(theta, new_point)
X_vector = self.poly_fit.get_coefficient_vector(positive_point, degree)
Y_predict = float(np.squeeze(np.dot(X_vector.T, parameter))) # 去掉矩阵的维度,变为一个浮点数
negative_point = np.zeros((2, 1))
negative_point[0, 0], negative_point[1, 0] = positive_point[0], Y_predict
result_point = self.trans.negative_transformation_point(theta, negative_point)
target_point = [result_point[0] + x_mean, result_point[1] + y_mean]
smoothing_dataset[i] = target_point
return smoothing_dataset
def main(self, points, smoothing_times):
xy_dataset = [[data[0], data[1]] for data in points] # 仅对投影坐标下的值进行平滑,z值不变
z_dataset = [data[2] for data in points]
for _ in range(smoothing_times):
xy_dataset = self.savgol(xy_dataset)
smoothing_dataset = [[xy_dataset[i][0], xy_dataset[i][1], z_dataset[i]] for i in range(len(z_dataset))]
return smoothing_dataset
# !/usr/bin/env python
# coding:utf-8
# 此文件由薛冰冰编码完成,功能是:由于库中的topo数据不满足规范要求,因而调用此程序进行修改
# 本人仅删除没有使用过的变量,以及调整格式
from osgeo import gdal, ogr
import re
class RefreshTopoNext(object):
def __init__(self):
self.id = "ID"
self.meshidname = 'MESHID'
self.nextname = "NEXT"
self.nextcountname = "NEXT_COUNT"
self.prevname = "PREV"
self.prevcountname = "PREV_COUNT"
self.layername = "HD_TOPO_P"
def run(self, datasoure=None, meshid=0):
layer = datasoure.GetLayerByName(self.layername)
if layer is None:
print("can not find indentify layer: " + str(layer.GetName()))
return False
afilter = '"' + self.meshidname + '" = ' + "'" + str(meshid) + "'"
layer.SetAttributeFilter(afilter)
feat = layer.GetNextFeature()
mesh_featdict = {}
while feat is not None:
fid = feat.GetFieldAsInteger64(self.id)
mesh_featdict[fid] = feat
feat = layer.GetNextFeature()
next_idx = layer.FindFieldIndex(self.nextname, 1)
nextcount_idx = layer.FindFieldIndex(self.nextcountname, 1)
prev_idx = layer.FindFieldIndex(self.prevname, 1)
prevcount_idx = layer.FindFieldIndex(self.prevcountname, 1)
if next_idx < 0 or nextcount_idx < 0 or prev_idx < 0 or prevcount_idx < 0:
print("error data")
return False
layer.SetAttributeFilter(afilter)
layer.ResetReading()
feat = layer.GetNextFeature()
update_fet_set = set()
while feat is not None:
prevstring = feat.GetFieldAsString(prev_idx)
res = filter(lambda x: len(x) > 0, re.split('[:)(]', prevstring.strip()))
prev_ids = list(res)
count = len(prev_ids)
i = 1
while i < count:
prev_fid = int(prev_ids[i])
if prev_fid not in mesh_featdict:
feat.SetFieldInteger64(prevcount_idx, 0)
update_fet_set.add(feat)
i = i + 1
nextstring = feat.GetFieldAsString(next_idx)
res = filter(lambda x: len(x) > 0, re.split('[:)(]', nextstring.strip()))
next_ids = list(res)
count = len(next_ids)
i = 1
while i < count:
next_fid = int(next_ids[i])
if next_fid not in mesh_featdict:
feat.SetFieldInteger64(nextcount_idx, 0)
update_fet_set.add(feat)
i = i + 1
feat = layer.GetNextFeature()
datasoure.StartTransaction()
for fet in update_fet_set:
layer.SetFeature(fet)
datasoure.CommitTransaction()
layer.SetAttributeFilter(None)
layer.ResetReading()
return True
# def main():
# tool = RefreshTopoNext()
# try:
# filepath = r"PG:dbname='test1' host='localhost' port='5432' user='postgres' password='19931018'"
# datasource = ogr.Open(filepath, gdal.OF_VECTOR | gdal.OF_UPDATE)
#
# if tool.run(datasoure=datasource, meshid=20002055):
# print(u"SUCCESS:刷新图幅边界上不在本图幅内的前驱后继关系")
# else:
# print(u"ERROR:刷新图幅边界上不在本图幅内的前驱后继关系")
# except BaseException as e:
# print(u"ERROR:刷新图幅边界上不在本图幅内的前驱后继关系")
# print(e)
#
#
# if __name__ == '__main__':
# main()
# _*_ coding: utf-8 _*_ #
# @Time: 2020/1/2 19:18
# @Author: XiongChao
import numpy as np
def get_coefficient_vector(point, n):
# 根据一个点,返回一个(n + 1) × 1的矩阵
vector = np.zeros(n + 1)
for i in range(n + 1):
vector[i] = np.power(point[0], i)
return vector.reshape(n + 1, -1) # 返回的对象是二维
def get_coefficient_matrix(dataset, n):
# 获取系数矩阵,为m × (n + 1)
m = len(dataset)
coefficient_matrix = np.zeros((m, n + 1))
for i in range(m):
for j in range(n + 1):
coefficient_matrix[i, j] = np.power(dataset[i][0], j)
return coefficient_matrix
def get_standard_Y_matrix(dataset):
# 获取原始的Y值矩阵,为m × 1
m = len(dataset)
Y_matrix = np.zeros((m, 1))
for i in range(m):
Y_matrix[i, 0] = dataset[i][1]
return Y_matrix
def poly_fitting_parameter(dataset, n):
# dataset中有m个点用于拟合,拟合的多项式的次数为n次
coefficient_matrix = get_coefficient_matrix(dataset, n)
Y_matrix = get_standard_Y_matrix(dataset)
result = np.linalg.det(np.dot(coefficient_matrix.T, coefficient_matrix))
if result == 0:
# 返回的最好是同一种数据结构,便于判断,比如Numpy数组没有 not array判断
return np.zeros(n)
result1 = np.linalg.inv(np.dot(coefficient_matrix.T, coefficient_matrix))
parameter = np.dot(np.dot(result1, coefficient_matrix.T), Y_matrix) # 为(n + 1) × 1矩阵
return parameter
def fitting_loss(dataset, n):
# 指定次数的多项式拟合损失
parameter = poly_fitting_parameter(dataset, n)
if parameter.shape == (n, ):
# 出现奇异矩阵的时候,默认损失为无穷
return np.inf
loss = 0
for data in dataset:
X_vector = get_coefficient_vector(data, n)
Y_predict = np.dot(X_vector.T, parameter)
loss += np.squeeze(data[1] - Y_predict) ** 2
return loss, parameter
def get_degree_of_polynomial(dataset):
# 返回损失值最小的多项式次数
degree_list = [1, 2] # 仅遍历到二次
loss_list = []
parameter_list = []
for n in degree_list:
loss, parameter = fitting_loss(dataset, n)
loss_list.append(loss)
parameter_list.append(parameter)
result = np.array([value == np.inf for value in loss_list])
if result.all():
# 遍历的次数下,构造的系数矩阵都为奇异矩阵
return False
degree = loss_list.index(min(loss_list))
optimization_parameter = parameter_list[degree]
return degree + 1, optimization_parameter
def data_normalization(dataset):
# 数据归一化,返回新数据和均值
x_dataset = [data[0] for data in dataset]
y_dataset = [data[1] for data in dataset]
x_mean = sum(x_dataset) / len(x_dataset)
y_mean = sum(y_dataset) / len(y_dataset)
new_dataset = [[data[0] - x_mean, data[1] - y_mean] for data in dataset]
return new_dataset, x_mean, y_mean
\ No newline at end of file
# _*_ coding: utf-8 _*_ #
# @Time: 2020/1/15 10:32
# @Author: XiongChao
# 对数据集进行正向旋转平移和反向旋转平移
import numpy as np
import math
def cos_(vector1, vector2):
# 计算两个向量的余弦值
cos_theta = (vector1[0] * vector2[0] + vector1[1] * vector2[1]) / \
(math.sqrt(vector1[0] ** 2 + vector1[1] ** 2) * math.sqrt(vector2[0] ** 2 + vector2[1] ** 2))
# 由于浮点运算的截断误差,可能导致求得的值大于1或者小于-1,因此需要进行处理
if cos_theta >= 1:
cos_theta = 1
elif cos_theta <= -1:
cos_theta = -1
return cos_theta
def angle_of_two_vector(vector1, vector2):
# 返回两个向量的角度,返回的值为弧度
cos_theta = cos_(vector1, vector2)
theta = math.acos(cos_theta)
return theta
def intersection_of_two_line(point1, point2, point3, point4):
# 获取两条直线的交点, point1和point2为一条线的起始点,point3和point4为一条直线的起始点
# 在这里,如果两条线平行,那么必然是数据有问题
result1 = (point2[1] - point1[1]) * (point4[0] - point3[0]) - (point4[1] - point3[1]) * (point2[0] - point1[0])
if result1 == 0:
return False
result2 = (point2[1] - point1[1]) * point1[0] - (point2[0] - point1[0]) * point1[1]
result3 = (point4[1] - point3[1]) * point3[0] - (point4[0] - point3[0]) * point3[1]
x = ((point4[0] - point3[0]) * result2 - (point2[0] - point1[0]) * result3) / result1
y = ((point4[1] - point3[1]) * result2 - (point2[1] - point1[1]) * result3) / result1
return x, y
def clockwise_counterclockwise(point1, point2, point3):
# 判断123是顺时针还是逆时针
# 计算向量point1point2与point2point3的向量积
result = (point2[0] - point1[0]) * (point3[1] - point2[1]) - (point2[1] - point1[1]) * (point3[0] - point2[0])
if result > 0:
# point1point2point3逆时针
return 1
else:
return 0
def rotation_angle(point1, point2):
# 计算坐标轴旋转的角度
vector1 = (point2[0] - point1[0], point2[1] - point1[1])
vector2 = (1, 0)
angle = angle_of_two_vector(vector1, vector2)
intersection = intersection_of_two_line(point1, point2, (0, 0), (1, 0))
if not intersection:
return angle
flag_point = [intersection[0] + vector1[0], intersection[1] + vector1[1]]
result = clockwise_counterclockwise((0, 0), (1, 0), flag_point)
if result == 0:
angle = 2 * np.pi - angle
return angle
def data_normalization(dataset):
# 数据归一化,返回新数据和均值
x_dataset = [data[0] for data in dataset]
y_dataset = [data[1] for data in dataset]
x_mean = sum(x_dataset) / len(x_dataset)
y_mean = sum(y_dataset) / len(y_dataset)
new_dataset = [[data[0] - x_mean, data[1] - y_mean] for data in dataset]
return new_dataset, x_mean, y_mean
def construct_positive_transformation_matrix(theta):
# theta为弧度值
positive_matrix = np.zeros((2, 2))
positive_matrix[0, 0] = np.cos(theta)
positive_matrix[0, 1] = np.sin(theta)
positive_matrix[1, 0] = -np.sin(theta)
positive_matrix[1, 1] = np.cos(theta)
return positive_matrix
def positive_transformation_point(theta, point):
# 正向变化一个点
coefficient_matrix = np.zeros((2, 1))
coefficient_matrix[0, 0], coefficient_matrix[1, 0] = point[0], point[1]
transformation_matrix = construct_positive_transformation_matrix(theta)
positive_matrix = np.dot(transformation_matrix, coefficient_matrix)
positive_point = [positive_matrix[0, 0], positive_matrix[1, 0]]
return positive_point
def construct_negative_transformation_matrix(theta):
# theta为弧度值
negative_matrix = np.zeros((2, 2))
negative_matrix[0, 0] = np.cos(theta)
negative_matrix[0, 1] = - np.sin(theta)
negative_matrix[1, 0] = np.sin(theta)
negative_matrix[1, 1] = np.cos(theta)
return negative_matrix
def negative_transformation_point(theta, point):
# 反向变换一个点
coefficient_matrix = np.zeros((2, 1))
coefficient_matrix[0, 0], coefficient_matrix[1, 0] = point[0], point[1]
transformation_matrix = construct_negative_transformation_matrix(theta)
negative_matrix = np.dot(transformation_matrix, coefficient_matrix)
negative_point = [negative_matrix[0, 0], negative_matrix[1, 0]]
return negative_point
def positive_transformation(dataset):
# 对数据集(点集)进行正向旋转平移变换,使用起点和终点构建方向
new_dataset, x_mean, y_mean = data_normalization(dataset)
n = len(dataset)
coefficient_matrix = np.zeros((2, n))
for i in range(n):
coefficient_matrix[:, i] = new_dataset[i]
theta = rotation_angle(new_dataset[0], new_dataset[-1])
transformation_matrix = construct_positive_transformation_matrix(theta)
positive_matrix = np.dot(transformation_matrix, coefficient_matrix)
m = len(positive_matrix[0])
positive_dataset = [[]] * m
for i in range(m):
positive_dataset[i] = list(positive_matrix[:, i])
return positive_dataset, theta, x_mean, y_mean
def negative_transformation(dataset, theta, x_mean, y_mean):
# 对数据集(点集)进行反向旋转平移变换,使用起点和终点构建方向
n = len(dataset)
coefficient_matrix = np.zeros((2, n))
for i in range(n):
coefficient_matrix[: i] = dataset[i]
transformation_matrix = construct_negative_transformation_matrix(theta)
mean_matrix = np.zeros((2, 1))
mean_matrix[0, 0] = x_mean
mean_matrix[1, 0] = y_mean
negative_matrix = np.dot(transformation_matrix, coefficient_matrix) + mean_matrix
m = len(coefficient_matrix[0])
negative_dataset = [[]] * m
for i in range(m):
negative_dataset[i] = list(negative_matrix[:, i])
return negative_dataset
# _*_ coding: utf-8 _*_ #
# @Time: 2020/1/2 19:19
# @Author: XiongChao
import numpy as np
from .polynomial_fitting import get_degree_of_polynomial, get_coefficient_vector
from .rotation_and_translation_of_coordinate import positive_transformation, negative_transformation_point, \
positive_transformation_point
def savgol(dataset, window_size=5):
m = len(dataset)
if m <= window_size:
return dataset
smoothing_dataset = [[]] * m # 存储最终的平滑数据
smoothing_dataset[0], smoothing_dataset[-1] = dataset[0], dataset[-1] # 保留首尾点
# 对开始几个点的平滑
positive_dataset, theta, x_mean, y_mean = positive_transformation(dataset[:window_size])
result = get_degree_of_polynomial(positive_dataset)
if result:
degree, parameter = result
# parameter = poly_fitting_parameter(positive_dataset, degree)
for i in range(1, window_size // 2):
point = dataset[i]
new_point = [point[0] - x_mean, point[1] - y_mean]
positive_point = positive_transformation_point(theta, new_point)
X_vector = get_coefficient_vector(positive_point, degree)
Y_predict = float(np.squeeze(np.dot(X_vector.T, parameter))) # 去掉矩阵的维度,变为一个浮点数
negative_point = np.zeros((2, 1))
negative_point[0, 0], negative_point[1, 0] = positive_point[0], Y_predict
result_point = negative_transformation_point(theta, negative_point)
target_point = [result_point[0] + x_mean, result_point[1] + y_mean]
smoothing_dataset[i] = target_point
# 对最后几个点的平滑
positive_dataset, theta, x_mean, y_mean = positive_transformation(dataset[-window_size:])
result = get_degree_of_polynomial(positive_dataset)
if result:
degree, parameter = result
# parameter = poly_fitting_parameter(positive_dataset, degree)
for j in range(m - (window_size - 1) // 2, m - 1):
point = dataset[j]
new_point = [point[0] - x_mean, point[1] - y_mean]
positive_point = positive_transformation_point(theta, new_point)
X_vector = get_coefficient_vector(positive_point, degree)
Y_predict = float(np.squeeze(np.dot(X_vector.T, parameter))) # 去掉矩阵的维度,变为一个浮点数
negative_point = np.zeros((2, 1))
negative_point[0, 0], negative_point[1, 0] = positive_point[0], Y_predict
result_point = negative_transformation_point(theta, negative_point)
target_point = [result_point[0] + x_mean, result_point[1] + y_mean]
smoothing_dataset[j] = target_point
# 获取中间点的平滑值
for i in range(window_size // 2, m - (window_size - 1) // 2):
data = dataset[i - window_size // 2: i + window_size // 2 + 1]
positive_dataset, theta, x_mean, y_mean = positive_transformation(data)
result = get_degree_of_polynomial(positive_dataset)
if result:
degree, parameter = result
# parameter = poly_fitting_parameter(positive_dataset, degree)
point = dataset[i]
new_point = [point[0] - x_mean, point[1] - y_mean]
positive_point = positive_transformation_point(theta, new_point)
X_vector = get_coefficient_vector(positive_point, degree)
Y_predict = float(np.squeeze(np.dot(X_vector.T, parameter))) # 去掉矩阵的维度,变为一个浮点数
negative_point = np.zeros((2, 1))
negative_point[0, 0], negative_point[1, 0] = positive_point[0], Y_predict
result_point = negative_transformation_point(theta, negative_point)
target_point = [result_point[0] + x_mean, result_point[1] + y_mean]
smoothing_dataset[i] = target_point
return smoothing_dataset
def automatic_smoothing(dataset, times):
# 对线几何的自动化平滑处理
XY_dataset = [[data[0], data[1]] for data in dataset] # 仅对投影坐标下的值进行平滑,z值不变
Z_dataset = [data[2] for data in dataset]
for i in range(times):
XY_dataset = savgol(XY_dataset)
smoothing_XY_dataset = XY_dataset
smoothing_dataset = [[smoothing_XY_dataset[i][0], smoothing_XY_dataset[i][1], Z_dataset[i]]
for i in range(len(Z_dataset))]
return smoothing_dataset
......@@ -7,14 +7,19 @@ import math
import numpy as np
from sklearn.neighbors import KDTree
from . import const
from .data_interpolation import interpolation_dataset
DEF_ALPHA = 0.0000001 # 单位米,如果是度的话,请进行转换
def two_point_distance(point1, point2):
def two_point_distance_2d(point1, point2):
return math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2)
def two_point_distance_3d(point1, point2):
# 计算三元坐标点的距离
return math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2 + (point2[2] - point1[2]) ** 2)
def cos_(vector1, vector2):
# 计算两个向量的余弦值
cos_theta = (vector1[0] * vector2[0] + vector1[1] * vector2[1]) / \
......@@ -34,6 +39,20 @@ def angle_of_two_vector(vector1, vector2):
return theta
def intersection_of_two_line(point1, point2, point3, point4):
# 获取两条直线的交点, point1和point2为一条线的起终点,point3和point4为一条直线的起终点
# 在这里,如果两条线平行,那么必然是数据有问题
result1 = (point2[1] - point1[1]) * (point4[0] - point3[0]) - (point4[1] - point3[1]) * (point2[0] - point1[0])
if result1 == 0:
return []
result2 = (point2[1] - point1[1]) * point1[0] - (point2[0] - point1[0]) * point1[1]
result3 = (point4[1] - point3[1]) * point3[0] - (point4[0] - point3[0]) * point3[1]
x = ((point4[0] - point3[0]) * result2 - (point2[0] - point1[0]) * result3) / result1
y = ((point4[1] - point3[1]) * result2 - (point2[1] - point1[1]) * result3) / result1
z = (point1[2] + point2[2]) / 2
return [x, y, z]
def three_point_dot_product(point1, point2, point3):
# 向量21与23的内积
result = (point1[0] - point2[0]) * (point3[0] - point2[0]) + \
......@@ -41,35 +60,18 @@ def three_point_dot_product(point1, point2, point3):
return result
def dis_from_point_to_line(k, b, point):
# k, b代表直线斜率,默认不与X轴垂直
dis = np.abs(k * point[0] + b - point[1]) / np.sqrt(1 + k * k)
return dis
def nearest_point_of_dataset(point, dataset):
# 在dataset中寻找距离point最近的点
# 返回的是dataset的下标
# 仅使用两坐标
distance_list = [two_point_distance(point[:2], data[:2]) for data in dataset]
return dataset[distance_list.index(min(distance_list))]
def clockwise_counterclockwise(point1, point2, point3):
# 判断123是顺时针还是逆时针
# 计算向量point1point2与point2point3的向量积
result = (point2[0] - point1[0]) * (point3[1] - point2[1]) - (point2[1] - point1[1]) * (point3[0] - point2[0])
if result > 0:
# point1point2point3逆时针
return 1
else:
return 0
def is_same_point_2d(point1, point2):
# 判断两个点是不是一个点,仅选择x, y
flag = np.array([np.abs(point1[i] - point2[i]) < 0.000001 for i in range(len(point1))])
flag = flag[:2] # 当为三元数值得时候,仅取前两位进行比较
if flag.all():
return True
return False
def is_same_point(point1, point2):
# 判断两个点是不是一个点
flag = np.array([np.abs(point1[i] - point2[i]) < const.ALPHA for i in range(len(point1))])
flag = flag[:2] # 当为三元数值得时候,仅取前两位进行比较
def is_same_point_3d(point1, point2):
# 判断两个点是不是一个点,选择x, y, z
flag = np.array([np.abs(point1[i] - point2[i]) < DEF_ALPHA for i in range(len(point1))])
if flag.all():
return True
return False
......@@ -94,78 +96,12 @@ def vertical_point(point1, point2, point3):
return x, y
def one_point_to_vertical_point_dis(point1, point2, point3):
# point2, point3确定一条直线,point1到此直线的垂点
ver_point = vertical_point(point1, point2, point3)
distance = two_point_distance(point1, ver_point)
return distance
# def one_point_to_vertical_point_dis(point1, point2, point3):
# # # point2, point3确定一条直线,point1到此直线的垂点
# # ver_point = vertical_point(point1, point2, point3)
# # distance = two_point_distance(point1, ver_point)
# # return distance
def nearest_point_index_of_dataset(point, dataset):
def nearest_m_point_of_dataset_2d(point, dataset, m):
# 在dataset中寻找距离point最近的点
# 仅使用两坐标
tree = KDTree(np.array(dataset), leaf_size=const.LEAF_SIZE)
dist, ind = tree.query([np.array(point[:2])], k=1)
return ind[0][0]
def nearest_two_point_of_dataset(point, dataset):
# 在dataset中寻找距离point最近的点
# 仅使用两坐标
tree = KDTree(np.array(dataset), leaf_size=const.LEAF_SIZE)
dist, ind = tree.query([np.array(point[:2])], k=2)
return [dataset[ind[0][0]], dataset[ind[0][1]]]
def dis_from_dataset_to_dataset(dataset1, dataset2):
# 两个数据集之间的距离,采取一个数据集的点到另一个数据集的距离的平均
# 数据集经过插值后,计算点到垂线的距离来度量宽度的误差会很小
# 首先保证同向
dataset1 = [[data[0], data[1]] for data in dataset1]
dataset2 = [[data[0], data[1]] for data in dataset2]
dataset1 = interpolation_dataset(dataset1, 2) # 插值
dataset2 = interpolation_dataset(dataset2, 2)
distance1 = two_point_distance(dataset1[0], dataset2[0])
distance2 = two_point_distance(dataset1[0], dataset2[-1])
if distance1 > distance2:
dataset2 = dataset2[::-1]
# 取对齐的两段
index1 = nearest_point_index_of_dataset(dataset1[0], dataset2)
index2 = nearest_point_index_of_dataset(dataset2[0], dataset1)
distance1 = two_point_distance(dataset1[0], dataset2[index1])
distance2 = two_point_distance(dataset2[0], dataset1[index2])
if distance1 > distance2:
dataset1 = dataset1[index2:]
else:
dataset2 = dataset2[index1:]
index3 = nearest_point_index_of_dataset(dataset1[-1], dataset2)
index4 = nearest_point_index_of_dataset(dataset2[-1], dataset1)
distance3 = two_point_distance(dataset1[-1], dataset2[index3])
distance4 = two_point_distance(dataset2[-1], dataset1[index4])
if distance3 > distance4:
dataset1 = dataset1[:index4 + 1]
else:
dataset2 = dataset2[:index3 + 1]
if len(dataset2) < 2:
# 仅有一个点,在此特殊情形下,仅作简单计算
point = dataset2[0]
index5 = nearest_point_index_of_dataset(point, dataset1)
target_distance = two_point_distance(point, dataset1[index5])
else:
distance_list = []
for i in range(0, len(dataset1)):
point1 = dataset1[i]
point2, point3 = nearest_two_point_of_dataset(point1, dataset2)
distance = one_point_to_vertical_point_dis(point1, point2, point3)
distance_list.append(distance)
target_distance = sum(distance_list) / len(distance_list)
return round(target_distance * 1000)
# 二维搜索
if len(point) == 3:
dataset = [[point1[0], point1[1]] for point1 in dataset]
tree = KDTree(np.array(dataset), leaf_size=20)
dist, ind = tree.query([np.array(point[:2])], k=m)
index_list = list(ind[0])
return index_list
......@@ -23,47 +23,67 @@ class _const:
c = _const()
c.BACKUP_MESH_BOX_FILE_NAME = "backup_mesh_box.gpkg" # 图幅框文件的备份文件
c.MESH_BOX_LAYER_NAME = "l13" # 图幅框文件的图层名
c.MESH_BOX_MESH_ID = "meshid" # 图幅框文件的图幅号字段
c.MESH_ID = "MESH_ID" # 地图数据的图幅号
c.LINK_ID = "LINK_ID"
c.LINK_IDS = "LINK_IDS"
c.EDGE_IDS = "EDGE_IDS"
c.LANE_IDS = "LANE_IDS"
c.SMOOTHING_MOUNT = 70 # 接边的时候在边界附近平滑的次数,单位是每米的平滑次数
c.START_NODE_ID = "START_NODE_ID"
c.END_NODE_ID = "END_NODE_ID"
c.NODE_ID = "NODE_ID"
c.HD_LINK_ID = "HD_LINK_ID"
c.START_EDGE_NODE_ID = "START_EDGE_NODE_ID"
c.END_EDGE_NODE_ID = "END_EDGE_NODE_ID"
c.START_LANE_NODE_ID = "START_LANE_NODE_ID"
c.END_LANE_NODE_ID = "END_LANE_NODE_ID"
c.LENGTH_THRESHOLD = 0.001 # 抽稀阈值
c.MESH_HD_LINK = "HD_LINK"
c.LANE_EDGE_ID = "LANE_EDGE_ID"
c.LEFT_LANE_EDGE_ID = "LEFT_LANE_EDGE_ID"
c.RIGHT_LANE_EDGE_ID = "RIGHT_LANE_EDGE_ID"
c.RECTANGLE_WIDTH = 30 # 设置buffer框的宽度为30,长已知
c.LINK_DIS_POINTS = 5 # 计算参考线之间距离所使用的点的个数
c.LINK_DIS_THRESHOLD = 4 # 衡量两个图幅的参考线之间距离的阈值
c.MESH_HD_LANE_EDGE = "HD_LANE_EDGE" # 边界线图层名
c.MESH_HD_LANE_LINK = "HD_LANE_LINK" # 中心线图层名
c.MESH_HD_NODE = "HD_NODE" # 参考线节点图层名
c.MESH_NODE_ID = "NODE_ID" # 参考线节点的主键
c.MESH_HD_LANE_NODE = "HD_LANE_NODE" # 中心线节点图层名
c.MESH_LANE_NODE_ID = "LANE_NODE_ID" # 中心线节点图层的主键
c.MESH_HD_LANE_EDGE_NODE = "HD_LANE_EDGE_NODE" # 边界线节点图层名
c.MESH_LANE_EDGE_NODE_ID = "LANE_EDGE_NODE_ID" # 边界线节点图层的主键
c.SMOOTHING_DATA_LENGTH = 6 # 截取多长的数据平滑
c.MESH_INTERPOLATION_MOUNT1 = 1 # 接边的时候插值
c.MESH_INTERPOLATION_MOUNT2 = 0.125 # 接边的时候插值
c.CONNECT_DIS_THRESHOLD = 3 # 衡量是否连接对应两条线的阈值,针对边界线和中心线
c.ELEVATION_THRESHOLD = 1.5 # 判断对应的两条路是否在同一平面
c.NEAR_POINT_THRESHOLD = 0.03 # 是否相接近判断的阈值
c.ALPHA = 0.00001 # 判断两个浮点数是否相等
c.LEAF_SIZE = 20
c.RETURNED_MESH_INFO_FILE_NAME = "未接边信息.txt"
# main.py中使用的常量
c.MESH_BOX_LAYER_NAME = "l13" # 图幅框的图层名
# connect_mesh.py中使用的常量
c.MESH_ID = "MESH_ID" # 图幅号
c.BUFFER_SIZE1 = 0.0005 # 指定的图幅框的buffer的大小
c.BUFFER_SIZE2 = 0.00008 # 根据相邻图幅框的共有顶点构造的buffer框的大小,近似8米
c.BUFFER_SIZE4 = 0.00005 # 根据对角的图幅框的共有顶点构造的buffer框的大小,近似4米
c.HD_TOPO_P = "HD_TOPO_P" # 拓扑点的图层名
c.HD_LINK = "HD_LINK" # 参考线图层名
c.HD_NODE = "HD_NODE" # 参考线节点图层名
c.HD_LANE_LINK = "HD_LANE_LINK" # 中心线图层名
c.HD_LANE_NODE = "HD_LANE_NODE" # 中心线节点图层名
c.HD_LANE_EDGE = "HD_LANE_EDGE" # 边界线图层名
c.HD_LANE_EDGE_NODE = "HD_LANE_EDGE_NODE" # 边界线节点图层名
c.HD_LINK_EDGE = "HD_LINK_EDGE" # 道路边缘图层名
c.HD_LINK_EDGE_NODE = "HD_LINK_EDGE_NODE" # 道路边缘节点图层名
c.PREV_COUNT = "PREV_COUNT" # 某个gps点的前面有几个节点
c.NEXT_COUNT = "NEXT_COUNT" # 某个gps点的后面有几个节点
c.AZIMUTH = "AZIMUTH" # 采集车的朝向角
c.PITCH = "PITCH"
c.ROLL = "ROLL"
c.NEXT = "NEXT" # topo的下一个gps点
c.PREV = "PREV" # topo点的上一个gps点
c.ID = "ID" # gps数据的主键
c.BUFFER_SIZE3 = 0.00015 # 根据topo构建buffer的大小,近似15米
c.LEAF_SIZE = 20 # kd树的参数
c.INTERPOLATION_COUNT1 = 5 # 计算topo点到边界线的距离的时候插值的大小,越大,计算越准确
c.ANGLE1 = 60 # 用于避免引入交叉道路的接边数据
c.DIS_THRESHOLD1 = 4 # 只取距离gps在此范围内的边界线
c.HD_LINK_ID = "HD_LINK_ID" # 关联的参考线
c.LINK_ID = "LINK_ID" # 参考线的主键
c.LANE_EDGE_ID = "LANE_EDGE_ID" # 边界线的主键
c.LANE_LINK_ID = "LANE_LINK_ID" # 中心线的主键
c.LEFT_LINK_EDGE = "LEFT_LINK_EDGE" # 参考线关联的左边缘
c.RIGHT_LINK_EDGE = "RIGHT_LINK_EDGE" # 参考线关联的右边缘
# c.DIS_THRESHOLD2 = 50
c.AZIMUTH_THRESHOLD = 3
c.DIS_THRESHOLD2 = 5
c.ANGLE2 = 20
c.LINK_EDGE_ID = "LINK_EDGE_ID" # 道路边缘图层的主键
c.INTERPOLATION_COUNT2 = 4
c.COMPARISON_NUM1 = 5 # 使用多少个cpt的y轴数据或者z轴数据去做对比
c.LINK_CONNECT_INTERVAL = 4 # 参考线的间隔在这个范围内就接边
c.ELEVATION_THRESHOLD = 1.6 # 连接处的高程差距超过这个值就认为是高架桥
c.MOVE_COUNT_THRESHOLD = 25 # 如果begin_topo移动30次后还没有找到对应的接边参考线,就不接边
c.START_NODE_ID = "START_NODE_ID" # 参考线的起点节点的id
c.END_NODE_ID = "END_NODE_ID" # 参考线的尾点节点的id
c.NODE_ID = "NODE_ID" # 参考线节点的主键
c.INTERPOLATION_COUNT3 = 2 # 接边时候在接边处的插值数量
c.COMPARISON_NUM2 = 5 # 使用多少个点做接边相关的操作
c.SMOOTHING_COUNT = 50 # 每米平滑得次数
c.INTERPOLATION_COUNT4 = 0.125 # 最终数据的插值阈值
c.START_EDGE_NODE_ID = "START_EDGE_NODE_ID" # 边界线的起点字段
c.END_EDGE_NODE_ID = "END_EDGE_NODE_ID" # 边界线的终点字段
c.START_LANE_NODE_ID = "START_LANE_NODE_ID" # 中心线的起点字段
c.END_LANE_NODE_ID = "END_LANE_NODE_ID" # 中心线的终点字段
c.CONNECT_THRESHOLD = 2 # 除了参考线之外,需要连接的两天线之间cpt下x坐标的最大间距,超过就不接边
c.LANE_EDGE_NODE_ID = "LANE_EDGE_NODE_ID" # 边界线节点的主键
c.LANE_NODE_ID = "LANE_NODE_ID" # 中心线节点的主键
c.LINK_EDGE_NODE_ID = "LINK_EDGE_NODE_ID" # 道路边缘节点的主键
sys.modules[__name__] = c
# _*_ coding: utf-8 _*_ #
# @Time: 2020/2/26 15:33
# @Author: XiongChao
from util import const
from .common import one_point_to_vertical_point_dis
from edge_match_src import remove_duplicate_data
class DouglasPeuker(object):
def __init__(self):
self.threshold = const.LENGTH_THRESHOLD
self.qualify_list = list()
self.disqualify_list = list()
def diluting(self, point_list):
"""
vacuate_layer
:param point_list:二维点列表
:return:
"""
if len(point_list) < 3:
self.qualify_list.extend(point_list[::-1])
else:
# 找到与收尾两点连线距离最大的点
max_distance_index, max_distance = 0, 0
for index, point in enumerate(point_list):
if index in [0, len(point_list) - 1]:
continue
distance = one_point_to_vertical_point_dis(point, point_list[0], point_list[-1])
if distance > max_distance:
max_distance_index = index
max_distance = distance
# 若最大距离小于阈值,则去掉所有中间点。 反之,则将曲线按最大距离点分割
if max_distance < self.threshold:
self.qualify_list.append(point_list[-1])
self.qualify_list.append(point_list[0])
else:
# 将曲线按最大距离的点分割成两段
sequence_a = point_list[:max_distance_index + 1]
sequence_b = point_list[max_distance_index:]
for sequence in [sequence_a, sequence_b]:
if len(sequence) < 3 and sequence == sequence_b:
self.qualify_list.extend(sequence[::-1])
else:
self.disqualify_list.append(sequence)
def models(self, point_list):
self.diluting(point_list)
while len(self.disqualify_list) > 0:
self.diluting(self.disqualify_list.pop())
target_dataset = remove_duplicate_data(self.qualify_list)
return target_dataset
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment