常用的地图点压缩
1、算法应用
道格拉斯-普克抽稀算法,是用来对大量冗余的图形数据点进行压缩以提取必要的数据点。
2、算法步骤
对每一条曲线的首末点虚连一条直线,求所有点与直线的距离,
并找出最大距离值dmax ,
用dmax与限差D相比:
若dmax < D ,这条曲线上的中间点所有舍去;
若dmax ≥D ,保留dmax 相应的坐标点,并以该点为界,把曲线分为两部分,对这两部分反复使用该方法。
控制限差值的大小可以控制抽稀的粒度。
3、算法精度
压缩精度取决于事先给定的阈值的大小,阈值越大,简化越多!
简化版
from math import sqrt, pow
def point_to_line_Distance(point_a, point_b, point_c):
"""
计算点a到点b c所在直线的距离
:param point_a:
:param point_b:
:param point_c:
:return:
"""
# 首先计算b c 所在直线的斜率和截距
if point_b[0] == point_c[0]:
return 9999999
slope = (point_b[1] - point_c[1]) / (point_b[0] - point_c[0])
intercept = point_b[1] - slope * point_b[0]
# 计算点a到b c所在直线的距离
distance = abs(slope * point_a[0] - point_a[1] + intercept) / sqrt(1 + pow(slope, 2))
return distance
def douglas_peuker(point_list, threshold):
"""
道格拉斯-普克抽稀算法
:param point_list: like [[122.358638, 30.280378], [122.359314, 30.280649]]
:param threshold: 0.003
:return:
"""
def diluting(point_list, threshold, qualify_list, disqualify_list):
"""
抽稀
:param threshold:
:param disqualify_list:
:param qualify_list:
:param point_list:二维点列表
:return:
"""
if len(point_list) < 3:
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 = point_to_line_Distance(point, point_list[0], point_list[-1])
if distance > max_distance:
max_distance_index = index
max_distance = distance
# 若最大距离小于阈值,则去掉所有中间点。 反之,则将曲线按最大距离点分割
if max_distance < threshold:
qualify_list.append(point_list[-1])
qualify_list.append(point_list[0])
else:
# 将曲线按最大距离的点分割成两段
sequence_a = point_list[:max_distance_index]
sequence_b = point_list[max_distance_index:]
for sequence in [sequence_a, sequence_b]:
if len(sequence) < 3 and sequence == sequence_b:
qualify_list.extend(sequence[::-1])
else:
disqualify_list.append(sequence)
return qualify_list, disqualify_list
def get_qualify_list(point_list, threshold):
qualify_list = list()
disqualify_list = list()
qualify_list, disqualify_list = diluting(point_list, threshold, qualify_list, disqualify_list)
while len(disqualify_list) > 0:
qualify_list, disqualify_list = diluting(disqualify_list.pop(), threshold, qualify_list, disqualify_list)
return qualify_list
# 当返回值长度小于4时,减小 threshold的值
if len(point_list) < 5:
return point_list
result = get_qualify_list(point_list, threshold)
if len(result) < 4:
while len(result) < 4:
threshold = threshold * 0.9
result = get_qualify_list(point_list, threshold)
if len(result) > len(point_list):
return point_list
return result
优化版:
# -*- coding: utf-8 -*-
from math import sqrt, pow
def point_to_line_Distance(point_a, point_b, point_c):
"""
计算点a到点b c所在直线的距离
:param point_a:
:param point_b:
:param point_c:
:return:
"""
# 首先计算b c 所在直线的斜率和截距
if point_b[0] == point_c[0]:
return 9999999
slope = (point_b[1] - point_c[1]) / (point_b[0] - point_c[0])
intercept = point_b[1] - slope * point_b[0]
# 计算点a到b c所在直线的距离
distance = abs(slope * point_a[0] - point_a[1] + intercept) / sqrt(1 + pow(slope, 2))
return distance
def douglas_peuker(point_list, threshold, lowerLimit=4, ceiling=None):
"""
道格拉斯-普克抽稀算法
:param point_list: like [[122.358638, 30.280378], [122.359314, 30.280649]]
:param threshold: 0.003
:param lowerLimit: 输出点个数必须大于lowerLimit, 如果没有, 不做设置 like: 4
:param ceiling: 输出点个数必须小于ceiling, 如果没有,不做设置 like: 2500
:return:
"""
def diluting(point_list, threshold, qualify_list, disqualify_list):
"""
抽稀
:param threshold:
:param disqualify_list:
:param qualify_list:
:param point_list:二维点列表
:return:
"""
if len(point_list) < 3:
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 = point_to_line_Distance(point, point_list[0], point_list[-1])
if distance > max_distance:
max_distance_index = index
max_distance = distance
# 若最大距离小于阈值,则去掉所有中间点。 反之,则将曲线按最大距离点分割
if max_distance < threshold:
qualify_list.append(point_list[-1])
qualify_list.append(point_list[0])
else:
# 将曲线按最大距离的点分割成两段
sequence_a = point_list[:max_distance_index]
sequence_b = point_list[max_distance_index:]
for sequence in [sequence_a, sequence_b]:
if len(sequence) < 3 and sequence == sequence_b:
qualify_list.extend(sequence[::-1])
else:
disqualify_list.append(sequence)
return qualify_list, disqualify_list
def get_qualify_list(point_list, threshold):
qualify_list = list()
disqualify_list = list()
qualify_list, disqualify_list = diluting(point_list, threshold, qualify_list, disqualify_list)
while len(disqualify_list) > 0:
qualify_list, disqualify_list = diluting(disqualify_list.pop(), threshold, qualify_list, disqualify_list)
return qualify_list
# 当输入点数小于5,直接输出
if len(point_list) < 5:
return point_list
result = get_qualify_list(point_list, threshold)
# 当返回值长度小于lowerLimit时,减小 threshold的值
if len(result) < lowerLimit:
while len(result) < lowerLimit:
threshold = threshold * 0.9
result = get_qualify_list(point_list, threshold)
# 当返回值长度大于ceiling时,增大 threshold的值
if ceiling and len(result) > ceiling:
while len(result) > ceiling:
threshold = threshold * 1.1
result = get_qualify_list(point_list, threshold)
if len(result) > len(point_list):
return point_list
return result