基于python的CAD等高线绘制

这是我本科在做《路基工程课程设计》挡土墙设计的时候,老师只给了原始地面数据点,因为疫情在家时间充裕,我就摸索了python画CAD,最终实现了绘制不同等高距的等高线绘制。
python面向对象编程。

基础知识

我这个代码只适合地面趋势简单的,通过差值得到固定整数高程点,然后将相同高程点依次连接。

note:

  • 当时是在windows先写的代码,因为要用到win32com.client这个库,所以我估计 Mac下应该用不了,但可以修改代码绘制dxf文件,不调用CAD。
  • 后续我再补充 Excel文件、输出CAD的文件及效果图

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# _*_ coding:utf-8- _*_

from pyautocad import APoint, Autocad, aDouble
import win32com.client
import numpy as np

dgj = 0.5 # 等高距
ctfs = 10 # 放大倍数,本身采取m为单位绘制,可能放大10倍后输出效果好些

"""
等高线计算
"""
#连接excel读取数据,这个地方可以做一个前处理,直接把excel数据作为数组或者numpy数组进行处理

xlapp = win32com.client.Dispatch("Excel.Application")
wb = xlapp.ActiveWorkbook
ws = xlapp.ActiveWorkbook.ActiveSheet
"""
K58+70向右走为前进方向,绘制在
"""

"""
First
定义一个类,输入 list,其元素为APoint
内部函数得到插值的数据点,打印点数据
"""
class nei_cha:
def __init__(self, pnts = [APoint(0,1)], dgj = 0.5, gu_ding_cha_zhi = "X" ):
# gu_ding_cz = "X" 意思是不进行固定插值操作
self.pnts = pnts
self.dgj = dgj
self.gu_ding_cha_zhi = gu_ding_cha_zhi

# 输入两个三维坐标,x/y是相等的,内插得到所需高程
# 返回的是,该区间内得到的点组成的list
def do_two_points(self, p1 = APoint(0), p2 = APoint(0)):
if self.gu_ding_cha_zhi == "X":
nmax = int(max(p1.z, p2.z) // self.dgj)
nmin = int(min(p1.z, p2.z) // self.dgj)
if nmax == nmin: # 这两个点之间内插不出来
return 0
else:
nmax = self.gu_ding_cha_zhi
nmin = self.gu_ding_cha_zhi - 1
if (p1.z - nmax) * (p2.z - nmin) > 0: # 固定插值不在区间内
return 0

if p1.x == p2.x: # x坐标一样,需要内插Z到y
"""
求直线方程,y
"""
k = (p1.y - p2.y)/(p1.z - p2.z)
b = p1.y - k * p1.z

if self.gu_ding_cha_zhi == "X":
ni = np.array(range(nmin, nmax)).reshape(int(nmax - nmin), 1)
zi = (ni + 1) * self.dgj
else:
ni = np.array([self.gu_ding_cha_zhi]).reshape(int(nmax - nmin), 1)
zi = ni
yi = np.around(k * zi + b, 3)
#yi = np.interp(zi, [p1.z, p2.z], [p1.y, p2.y])
xi = np.full((len(yi), 1), p1.x)
pts_np = np.hstack((xi, yi, zi))
return pts_np

else: # y坐标一样,需要内插Z得到x
return 0
def do_it(self):
pnts_np = np.array([])
for i in range(len(self.pnts) - 1):

p_np_i = self.do_two_points(self.pnts[i], self.pnts[i+1])
if type(p_np_i) != type(0):
try:
pnts_np = np.vstack((pnts_np, p_np_i))
except:
pnts_np = p_np_i
if len(pnts_np) != len(np.array([])):
return pnts_np
else:
return 0



# points是原始地面点高程
ground_points = []
row = 2

while row <= 20:
i = 6 # 左侧数据
j = 8 # 右侧数据

# 桩号点绘图坐标
x0 = ((row - 2) / 3) * 10 + 70
y0 = 0
z0 = ws.cells(row, 7).value
p0 = APoint(x0, y0, z0)
pts = []
pts.append(p0)


# 拾取左侧点数据
while i >= 1:
try:
z_ = ws.cells(row, i).value
y_ = ws.cells(row + 1, i).value
pti = pts[-1] + (0, y_, z_)
pts.append(pti)
except:
pass
i -= 1
# 拾取右侧点数据
while ws.cells(row, j).value:
try:
z_ = ws.cells(row, j).value
y_ = - ws.cells(row + 1, j).value
ptj = pts[0] + (0, y_, z_)
pts.insert(0, ptj)
except:
print("右侧点拾取失败了:cells(%d,%d)"% (row, j))
pass
j += 1
ground_points.append(pts)

for i in pts:
print(i)
print("")
row = row + 3

# 利用上方点开始使用类进行,插值
all_np = []
for i in ground_points:
cha_zhi_la = nei_cha(i, dgj)
a = cha_zhi_la.do_it()
try:
all_np = np.vstack((all_np, a))
except:
all_np = a
pts_all = [APoint(list(i)) for i in all_np]
pts_all.sort(key = lambda x:x.z)
del all_np

h_min = pts_all[0].z
h_max = pts_all[-1].z


zzzi = h_min
sorted_pts = []
while zzzi <= h_max:
_ = [i for i in pts_all if i.z == zzzi]
_.sort(key = lambda x:x[0])
sorted_pts.append(_)
zzzi += dgj
del pts_all
for i in sorted_pts:
elevation = i[0].z
print("高程为 %s 的数据点"% elevation)
for l_ in i:
print(l_)
# 连接cad,并输出结果

del sorted_pts[0:11]
del sorted_pts[-10:-1]
acad = Autocad(create_if_not_exists=True)
acad.prompt("connect with the Autocad successfully.")
print("connect with the Autocad successfully.")

dwg = acad.ActiveDocument

# 定义等高线图层
dgx = acad.doc.layers.add("等高线")
dgx.LineWeight = 25
dwg.ActiveLayer = dgx
dgxbz = acad.doc.layers.add("等高线标注")


for m in range(len(sorted_pts)):
i = sorted_pts[m]
elevation = i[0].z
print("高程为 %s 的数据点"% elevation)
for l_ in i:
print(l_)
try:


for j in i:
j.z = 0
pt = aDouble([j for _ in i for j in _ * ctfs])
hl = acad.model.AddPolyLine(pt)
hl.LineWeight = 25
hl.Elevation = elevation
hl.Thickness = 900
if m % 5 == 0:
hl.LineWeight = 100
hl.color = 1
text_string = elevation
insertpt = i[0] * ctfs
textobj = acad.model.addMtext(insertpt, 26, text_string)
textobj.Height = 8
textobj.BackgroundFill = True
textobj.color = 1
textobj.layer = "等高线标注"

except:
print('*' * 10, "高程为 %s 的数据点不足,跳过绘制此等高线"% elevation)
pass