From 419bd200928c15cfec04e6cad66ca2700afa894d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=AF=E5=86=AC=E5=8D=AB?= Date: Fri, 26 May 2023 21:23:23 +0800 Subject: [PATCH] 3D plot --- .idea/.name | 2 +- 3d_plot_1.py | 639 +++++++++++++++++++++++++++ QT/3d_plot_2.ui | 385 ++++++++++++++++ __pycache__/creat_3d.cpython-310.pyc | Bin 0 -> 4217 bytes creat_3d.py | 105 +++++ 5 files changed, 1130 insertions(+), 1 deletion(-) create mode 100644 3d_plot_1.py create mode 100644 QT/3d_plot_2.ui create mode 100644 __pycache__/creat_3d.cpython-310.pyc create mode 100644 creat_3d.py diff --git a/.idea/.name b/.idea/.name index c659c60..bbcf0fd 100644 --- a/.idea/.name +++ b/.idea/.name @@ -1 +1 @@ -main_2.py \ No newline at end of file +creat_3d.py \ No newline at end of file diff --git a/3d_plot_1.py b/3d_plot_1.py new file mode 100644 index 0000000..db23edf --- /dev/null +++ b/3d_plot_1.py @@ -0,0 +1,639 @@ +# -*- coding: utf-8 -*- +import sys +from PySide6.QtWidgets import QApplication, QMainWindow, QGraphicsView, QFileDialog, QColorDialog +from PySide6.QtUiTools import QUiLoader +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar +from matplotlib.figure import Figure +import matplotlib.pyplot as plt +from PySide6.QtWidgets import QVBoxLayout +import creat_3d +import numpy as np +import matplotlib as mpl +from matplotlib.path import Path +from matplotlib.patches import Patch +from PySide6.QtGui import QPalette +import random +from scipy.spatial import ConvexHull +# plt.legend(prop={'family': 'SimHei', 'size': 15}) +from mpl_toolkits.mplot3d.art3d import Poly3DCollection + + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.toolbar = None + self.canvas = None + self.file_paths = [] + self.get_color = lambda n: list(map(lambda i: "#" + "%06x" % random.randint(0, 0xFFFFFF),range(n))) + self.len_color = ['k','k'] # 图例颜色选择值 + self.sho_color = ['k','k'] + self.ove_color = ['b','b'] + self.colorbar = 'jet' + # self.num = len(self.color) # 记录选择颜色的次数 + self.oilwell_color = 'k' + self.waterwell_color = 'k' + self.chose = 0 + self.len_num, self.short_num, self.over_num = 0,0,0 + self.box = 0 + self.axes = 0 + self.angle_axes = 0 + self.length_axes = 0 + self.datachange = 0 + self.wellchange = 0 + self.waterwell_chose=0 + self.oilwell_chose=0 + self.scatter = 0 + self.all_axes = 0 + self.len_axes_r, self.sho_axes_r,self.ver_axes_up=0,0,0 + self.axes_size = 8 + self.len_view=0 + self.short_view=0 + self.over_view=0 + # 加载UI文件 + self.ui = QUiLoader().load('QT/3d_plot_2.ui') + self.color_bar() + self.ui.colorbar_chose.currentTextChanged.connect(self.update_colorbar) # 更新colorbar + self.ui.chose_oilwell_color.clicked.connect(lambda: self.oil_well_color()) # 选择油井颜色 + self.ui.chose_oilwell_color.clicked.connect(lambda: self.plot_density()) # 更新油井颜色 + self.ui.chose_waterwell_color.clicked.connect(lambda: self.water_well_color()) # 选择水井颜色 + self.ui.chose_waterwell_color.clicked.connect(lambda: self.plot_density()) # 更新水井颜色 + # 更新图窗 + self.ui.graphicsView.repaint() + self.ui.graphicsView.update() + self.graphics_layout = QVBoxLayout(self.ui.graphicsView) + + self.ui.chose_datafile_button.clicked.connect(self.choose_datafile) + self.ui.chose_wellfile_button.clicked.connect(self.choose_wellfile) + + self.ui.data_file_lineEdit.textChanged.connect(lambda: self.data_change()) + self.ui.well_file_lineEdit.textChanged.connect(lambda: self.well_change()) + # 绘制或清除数据和画布 + self.ui.clear_data.clicked.connect(lambda: self.clear_data()) + + # 绘图设置中心比例 + self.ui.center_scale.clicked.connect(lambda: self.center_scale()) + self.ui.center_scale.clicked.connect(lambda: self.plot_density()) + self.ui.level_chose.valueChanged.connect(lambda: self.plot_density()) + # self.ui.gWeight_chose.valueChanged.connect(lambda: self.plot_density()) + + # 绘制bounding——box + self.ui.pushButton.clicked.connect(lambda: self.bounding_box()) + + # self.ui.pushButton_2.clicked.connect(lambda: self.Axes()) + + self.ui.pushButton_3.clicked.connect(lambda: self.axes_angles()) + self.ui.axes_length.clicked.connect(lambda: self.axes_length()) + self.ui.scatter_density.clicked.connect(lambda: self.scatter_density()) + self.ui.all_axes.clicked.connect(lambda: self.all_axes_plot()) + self.ui.axes_wordsize.valueChanged.connect(lambda: self.plot_density()) + self.ui.len_view.clicked.connect(lambda: self._len_view()) + self.ui.short_view.clicked.connect(lambda: self._short_view()) + self.ui.over_view.clicked.connect(lambda: self._over_view()) + self.ui.len_axes.clicked.connect(lambda: self._len_axes()) + self.ui.short_axes.clicked.connect(lambda: self._short_axes()) + self.ui.over_axes.clicked.connect(lambda: self._over_axes()) + self.ui.len_color.clicked.connect(self.len_color_chose) # 选择长轴颜色 + self.ui.short_color.clicked.connect(self.sho_color_chose) # 选择长轴颜色 + self.ui.over_color.clicked.connect(self.ove_color_chose) # 选择长轴颜色 + + def _len_axes(self): + if self.len_axes_r==0: + self.len_axes_r = 1 + else: + self.len_axes_r=0 + self.plot_density() + + def _short_axes(self): + if self.sho_axes_r==0: + self.sho_axes_r=1 + else: + self.sho_axes_r=0 + self.plot_density() + + def _over_axes(self): + if self.ver_axes_up==0: + self.ver_axes_up=1 + else: + self.ver_axes_up=0 + self.plot_density() + + def _len_view(self): + if self.len_view==0: + self.len_view=1 + self.over_view = 0 + self.short_view = 0 + else: + self.len_view=0 + self.plot_density() + + def _short_view(self): + if self.short_view==0: + self.short_view=1 + self.over_view = 0 + self.len_view = 0 + else: + self.short_view=0 + self.plot_density() + + def _over_view(self): + if self.over_view == 0: + self.over_view = 1 + self.len_view = 0 + self.short_view = 0 + else: + self.over_view = 0 + self.plot_density() + + def all_axes_plot(self): + if self.all_axes==0: + self.all_axes=1 + else: + self.all_axes=0 + self.plot_density() + + def scatter_density(self): + if self.scatter==0: + self.scatter=1 + else: + self.scatter=0 + self.plot_density() + + def data_change(self): + if self.datachange == 1: + self.plot_density() + self.dat_c = 0 + + def well_change(self): + if self.wellchange == 1: + self.waterwell_chose=1 + self.oilwell_chose=1 + self.plot_density() + self.well_c = 0 + + def bounding_box(self): + if self.box == 0: + self.box = 1 + else: + self.box = 0 + self.plot_density() + + def Axes(self): + if self.axes==0: + self.axes=1 + else: + self.axes=0 + self.plot_density() + + def axes_angles(self): + if self.waterwell_chose==0: + self.waterwell_chose=1 + else: + self.waterwell_chose=0 + self.plot_density() + + def axes_length(self): + if self.length_axes==0: + self.length_axes=1 + else: + self.length_axes=0 + self.plot_density() + + def self_levels(self): + self.levels = self.ui.level_chose.value() # 选择层参数 + self.plot_density() + + def self_gWeight(self): + self.gWeight = self.self.ui.gWeight_chose.value() + self.plot_density() + + def scene_fig(self): + fig = Figure() + # 在Figure对象中添加子图 + ax = fig.add_subplot(111) + return fig, ax + + def canvas_adjust(self, fig): + if self.canvas is not None: + # 从布局中删除旧的 canvas 和 toolbar + item = self.graphics_layout.takeAt(0) + while item: + widget = item.widget() + if widget: + widget.setParent(None) + item = self.graphics_layout.takeAt(0) + self.graphics_layout.removeWidget(self.canvas) + self.canvas = FigureCanvas(fig) + self.toolbar = NavigationToolbar(self.canvas, self.ui.graphicsView) + self.toolbar.setParent(self.canvas) + self.graphics_layout.addWidget(self.canvas) + self.adjustSize() + + def choose_datafile(self): + self.datachange = 0 + file_dialog = QFileDialog(self) + file_dialog.setFileMode(QFileDialog.ExistingFiles) + if file_dialog.exec(): + self.file_paths += file_dialog.selectedFiles() + self.ui.data_file_lineEdit.setText(','.join(self.file_paths)) + self.plot_density() + + def choose_wellfile(self): + self.wellchange=0 + self.waterwell_chose=1 + self.oilwell_chose=1 + file_dialog = QFileDialog(self) + file_dialog.setFileMode(QFileDialog.ExistingFiles) + if file_dialog.exec(): + file_paths = file_dialog.selectedFiles() + self.ui.well_file_lineEdit.setText(','.join(file_paths)) + self.plot_density() + + def len_color_chose(self): + col = QColorDialog.getColor() + self.len_color[self.len_num] = col.name() + self.len_num += 1 + self.plot_density() + + def sho_color_chose(self): + col = QColorDialog.getColor() + self.sho_color[self.short_num] = col.name() + self.short_num += 1 + self.plot_density() + + def ove_color_chose(self): + col = QColorDialog.getColor() + self.ove_color[self.over_num] = col.name() + self.over_num += 1 + self.plot_density() + + def oil_well_color(self): + col = QColorDialog.getColor() + self.oilwell_color = col.name() + + def water_well_color(self): + col = QColorDialog.getColor() + self.waterwell_color = col.name() + + def color_bar(self): + self.ui.colorbar_chose.addItem("jet") + self.ui.colorbar_chose.addItem("viridis") + self.ui.colorbar_chose.addItem("coolwarm") + self.ui.colorbar_chose.addItem('plasma') + self.ui.colorbar_chose.addItem("magma") + self.ui.colorbar_chose.addItem("inferno") + + def update_colorbar(self): + colorbar_name = self.ui.colorbar_chose.currentText() + self.colorbar = plt.get_cmap(colorbar_name) + self.plot_density() + + def clear_data(self): + self.file_paths = [] + self.len_color = ['k', 'k'] + self.sho_color = ['k', 'k'] + self.ove_color = ['b', 'b'] + self.len_num, self.short_num, self.over_num = 0, 0, 0 + self.chose = 0 + self.box = 0 + self.axes = 0 + self.waterwell_chose = 0 + self.oilwell_chose = 0 + self.length_axes = 0 + self.datachange = 0 + self.wellchange = 0 + self.scatter = 0 + self.all_axes = 0 + self.len_axes_r, self.sho_axes_r, self.ver_axes_up = 0, 0, 0 + self.axes_size = 8 + self.len_view = 0 + self.short_view = 0 + self.over_view = 0 + self.ui.data_file_lineEdit.clear() + self.ui.well_file_lineEdit.clear() + self.canvas.figure.clf() # 清除画布上的内容 + self.canvas.draw() + + def center_scale(self): + if self.oilwell_chose == 0: + self.oilwell_chose = 1 + else: + self.oilwell_chose = 0 + # self.num = 0 + + def plot_density(self): + fig, ax = self.scene_fig() + data_name = self.ui.data_file_lineEdit.text().split(',') # 文件输入 =================1 + level = self.ui.level_chose.value() # 层参数 =================2 + self.datachange=1 + data = creat_3d.create() + legend_elements = [] + if self.len_num == 2: + self.len_num = 0 + if self.short_num ==2: + self.short_num = 0 + if self.over_num==2: + self.over_num=0 + for i in range(len(data_name)): + data.data_pre(data_name[i]) + data.contours_pre(level) + vertices = data.vertices + faces = data.faces + """画出密度等高线""" + # 绘制等值面 + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + if self.scatter == 1: + vertices = data.data + x = data.data[:, 0] + y = data.data[:, 1] + z = data.data[:, 2] + ax.scatter(x, y, z, s=5, alpha=0.5) + else: + ax.plot_trisurf(vertices[:, 0], vertices[:, 1], faces, vertices[:, 2], + cmap=self.colorbar, alpha=0.4, edgecolor='none') + + # 添加三维网格对象填充曲面内部 + mesh = Poly3DCollection(vertices[faces], alpha=0.2, cmap=self.colorbar) + ax.add_collection3d(mesh) + ax.set_xlim(vertices[:, 0].min()-100, vertices[:, 0].max()+100) + ax.set_ylim(vertices[:, 1].min() - 100, vertices[:, 1].max() + 100) + ax.set_zlim(vertices[:, 2].min() - 100, vertices[:, 2].max() + 100) + # 设置坐标轴标签 + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + ax.grid(None) + + # self.OBB_3dbox(vertices, ax) + wells_name = self.ui.well_file_lineEdit.text() # 井文件输入 =================6 + if wells_name == '': + z_value = vertices[:, 2].min()/2+vertices[:, 2].max()/2 + self.OBB_3dbox(vertices, ax, z_value) + print('ax.azim {}'.format(ax.azim)) + + print('ax.elev {}'.format(ax.elev)) + self.canvas_adjust(fig) + else: + wells = creat_3d.well_to_edge() + wells.wells_name_and_position(wells_name) + self.wellchange = 1 + + """画井位信息""" + typee = wells.type + points = wells.position + namee = wells.name + for i in range(len(points)): + if typee[i][0] == 0 and self.oilwell_chose==1: + # oil_color = 'black' # 输入油井颜色选择 =================8 + ax.scatter(points[i][0], points[i][1], points[i][2], marker='o', edgecolors=self.oilwell_color, facecolors='none', s=70) + ax.scatter(points[i][0], points[i][1], points[i][2], marker='o', edgecolors=self.oilwell_color, facecolors='none', s=40, + linewidths=0.5) + ax.scatter(points[i][0], points[i][1], points[i][2], marker='o', edgecolors=self.oilwell_color, facecolors='none', s=20) + ax.plot([points[i][0], points[i][0]], [points[i][1], points[i][1]], + [points[i][2], points[i][2] - 100], color='r') + ax.text(points[i][0], points[i][1], points[i][2] - 100, f'{namee[i][0]}', + fontdict={'size': '8', 'color': 'b'}, zorder=len(data_name) + 2) # 井名信息 + elif typee[i][0]==1: + if self.waterwell_chose == 1: + ax.scatter(points[i][0], points[i][1], points[i][2], marker='o', edgecolors=self.waterwell_color, facecolors=self.waterwell_color, s=70, zorder=len(data_name)+1) + ax.plot([points[i][0],points[i][0]],[points[i][1],points[i][1]],[points[i][2],points[i][2]-100], color='r') + ax.text(points[i][0], points[i][1], points[i][2] -100, f'{namee[i][0]}', + fontdict={'size': '8', 'color': 'b'}, zorder=len(data_name) + 2) # 井名信息 + continue + self.OBB_3dbox(vertices, ax, points[i][2]) + self.canvas_adjust(fig) + + def OBB_3dbox(self, points, ax, z_value): + """ + 计算三维数据点的OBB最小包围盒 + 参数: + points: 三维数据点数组,形状为(N, 3) + 返回: + obb_center: OBB包围盒中心点,形状为(3,) + obb_axes: OBB包围盒坐标轴,形状为(3, 3) + obb_extents: OBB包围盒各坐标轴的长度,形状为(3,) + """ + # 计算凸包 + hull = ConvexHull(points) + hull_points = points[hull.vertices, :] + + # 计算凸包的质心 + hull_center = np.mean(hull_points, axis=0) + + # 计算凸包的协方差矩阵 + hull_cov = np.cov(hull_points, rowvar=False) + + # 对协方差矩阵进行SVD分解 + u, s, vt = np.linalg.svd(hull_cov) + + # 计算OBB包围盒的坐标轴和长度 + axes = vt.T + # 将点云变换到以重心为原点的坐标系下 + transformed_points = np.dot(points, vt) + # x,y,z = transformed_points[:,0],transformed_points[:,1],transformed_points[:,2] + x, y, z = points[:, 0], points[:, 1], points[:, 2] + # 计算变换后的点云的最小包围盒 + min_point = np.min(transformed_points, axis=0) + max_point = np.max(transformed_points, axis=0) + x_max, y_max, z_max = max_point[0], max_point[1], max_point[2] + x_min, y_min, z_min = min_point[0], min_point[1], min_point[2] + x_length = x_max - x_min + y_length = y_max - y_min + z_length = z_max - z_min + extents = [x_length, y_length, z_length] + center_new = min_point/2+max_point/2 # 新坐标轴下中心点坐标 + center = np.dot(center_new, axes) + + # 八个顶点坐标 + vertices = np.array([ + [x_min, y_min, z_min], [x_max, y_min, z_min], [x_max, y_max, z_min], [x_min, y_max, z_min], + [x_min, y_min, z_max], [x_max, y_min, z_max], [x_max, y_max, z_max], [x_min, y_max, z_max] + ]) + vertices = np.dot(vertices, axes) + # 计算所有棱的端点坐标 + ed_1 = [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3] + ed_2 = [1, 2, 3, 0, 5, 6, 7, 4, 4, 5, 6, 7] + if self.box==1: + for i, j in zip(np.array(ed_1), np.array(ed_2)): + ax.plot([vertices[i][0], vertices[j][0]], [vertices[i][1], vertices[j][1]], zs=[vertices[i][2], vertices[j][2]], color='grey') + + self.plot_axes(ax, axes, vertices, z_value) + return center, axes, extents + + def plot_axes(self, ax, axes, vertices, z): + # 标注轴向 + direct1 = vertices[0] - vertices[1] + direct2 = vertices[2] - vertices[1] + direct3= vertices[4] - vertices[0] + ang_1 = np.arccos(np.dot(direct1, np.array(([1, 0, 0]))) / np.linalg.norm(direct1)) / np.pi * 180 + ang_2 = np.arccos(np.dot(direct2, np.array(([0, 1, 0]))) / np.linalg.norm(direct2)) / np.pi * 180 + ang_3 = np.arccos(np.dot(direct3, np.array(([0, 0, 1]))) / np.linalg.norm(direct3)) / np.pi * 180 + if self.over_view==1: + ax.view_init(elev=90, azim=0) # 俯视图 + + print(np.dot(direct1,direct2), ang_1, ang_2, ang_3) + # 标注长短轴 + if np.linalg.norm(vertices[0] - vertices[1]) >= np.linalg.norm(vertices[1] - vertices[2]): + label1 = '长轴' + label2 = '短轴' + # 垂直长轴方向 + if self.len_view==1: + if vertices[0][0] > 0: + ax.view_init(elev=ang_3, azim=90 - ang_2) + else: + ax.view_init(elev=ang_3, azim=90 - ang_1) + + # # 垂直短轴方向 + if self.short_view==1: + if vertices[0][0] > 0: + ax.view_init(elev=ang_3, azim=ang_1+180) + else: + ax.view_init(elev=ang_3, azim=ang_2+180) + else: + label1 = '短轴' + label2 = '长轴' + # 垂直长轴方向 + if self.len_view==1: + if vertices[0][0] > 0: + ax.view_init(elev=ang_3, azim=360-ang_1) + else: + ax.view_init(elev=ang_3, azim=360-ang_2) + # # 垂直短轴方向 + if self.short_view==1: + if vertices[0][0] > 0: + ax.view_init(elev=ang_3, azim=270 - ang_1) + else: + ax.view_init(elev=ang_3, azim=270 - ang_2) + # 画长短轴 + center = [0, 0, z] + if (self.all_axes == 1 or self.len_axes_r==1) and label1 == '长轴': + ax.quiver(*center, *np.dot(axes[0],vertices[0]-center)*axes[0] + ,color=self.len_color[0], length=1, arrow_length_ratio=0.01) + ax.quiver(*center, *np.dot(axes[0], vertices[2]-center) * axes[0], + color=self.len_color[1], length=1, arrow_length_ratio=0.01) + elif (self.all_axes == 1 or self.len_axes_r==1) and label2 == '长轴': + ax.quiver(*center, *np.dot(axes[1], vertices[1]-center) * axes[1], + color=self.len_color[0], length=1, arrow_length_ratio=0.01) + ax.quiver(*center, *np.dot(axes[1], vertices[3]-center) * axes[1], + color=self.len_color[1], length=1, arrow_length_ratio=0.01) + if (self.all_axes == 1 or self.sho_axes_r==1) and label1 == '短轴': + ax.quiver(*center, *np.dot(axes[0], vertices[0] - center) * axes[0] + , color=self.sho_color[0], length=1, arrow_length_ratio=0.01) + ax.quiver(*center, *np.dot(axes[0], vertices[2] - center) * axes[0], + color=self.sho_color[1], length=1, arrow_length_ratio=0.01) + elif (self.all_axes == 1 or self.sho_axes_r==1) and label2 == '短轴': + ax.quiver(*center, *np.dot(axes[1], vertices[1] - center) * axes[1], + color=self.sho_color[0], length=1, arrow_length_ratio=0.01) + ax.quiver(*center, *np.dot(axes[1], vertices[3] - center) * axes[1], + color=self.sho_color[1], length=1, arrow_length_ratio=0.01) + if self.all_axes == 1 or self.ver_axes_up == 1: + ax.quiver(*center, *axes[2] * np.dot(axes[2], vertices[4] - center), color=self.ove_color[0], length=1, + arrow_length_ratio=0.01) + ax.quiver(*center, *axes[2] * np.dot(axes[2], vertices[0] - center), color=self.ove_color[1], length=1, + arrow_length_ratio=0.01) + if self.length_axes == 1: + len_1 = np.abs(np.dot(axes[0], vertices[0]-center)) + len_2 = np.abs(np.dot(axes[0], vertices[2]-center)) + len_3 = np.abs(np.dot(axes[1], vertices[1]-center)) + len_4 = np.abs(np.dot(axes[1], vertices[3]-center)) + if vertices[0][0]-vertices[1][0] >= 0: + lb1 = '右半轴长' + lb2 = '左半轴长' + l1 = 3/4 + l2 = 6/5 + else: + lb1 = '左半轴长' + lb2 = '右半轴长' + l1 = 6 / 5 + l2 = 3 / 4 + if vertices[2][0]-vertices[1][0] >= 0: + lb3 = '右半轴长' + lb4 = '左半轴长' + l3 = 3 / 4 + l4 = 6 / 5 + else: + lb3 = '左半轴长' + lb4 = '右半轴长' + l3 = 6 / 5 + l4 = 3 / 4 + lz = 1 + if ang_1<=90: + rota1=-ang_1 + else: + rota1=180-ang_1 + if ang_2<=90: + rota2=ang_2 + else: + rota2=180-ang_2 + word_size = self.ui.axes_wordsize.value() + if (self.all_axes == 1 or self.len_axes_r == 1) and label1 == '长轴': + ax.text(center[0] + l1 * (vertices[0][0] / 2 - vertices[1][0] / 2), + center[1] + l1 * (vertices[0][1] / 2 - vertices[1][1] / 2), + center[2] + lz * (vertices[0][2] / 2 - vertices[1][2] / 2), + label1+lb1 + f'{round(len_1,2)}m', + fontdict={'size': word_size, 'color': self.len_color[0], 'family': 'SimHei'}, rotation=rota1) # 标出距离 + ax.text(center[0]+l2*(-vertices[0][0] / 2 + vertices[1][0] / 2), + center[1] + l2*(-vertices[0][1] / 2 + vertices[1][1] / 2), + center[2] + lz * (-vertices[0][2] / 2 + vertices[1][2] / 2), + label1+ lb2 + f'{round(len_2, 2)}m', + fontdict={'size': word_size, 'color': self.len_color[1], 'family': 'SimHei'}, rotation=rota1) # 标出距离 + elif (self.all_axes == 1 or self.len_axes_r == 1) and label2 == '长轴': + ax.text(center[0] + l3 * (vertices[2][0] / 2 - vertices[1][0] / 2), + center[1] + l3 * (vertices[2][1] / 2 - vertices[1][1] / 2), + center[2] + lz * (vertices[2][2] / 2 - vertices[1][2] / 2), + label2+lb3 + f'{round(len_4, 2)}m', + fontdict={'size': word_size, 'color': self.len_color[1], 'family': 'SimHei'}, rotation=rota2) # 标出距离 + ax.text(center[0] + l4 * (-vertices[2][0] / 2 + vertices[1][0] / 2), + center[1] + l4 * (-vertices[2][1] / 2 + vertices[1][1] / 2), + center[2] + lz * (-vertices[2][2] / 2 + vertices[1][2] / 2), + label2 + lb4 + f'{round(len_3, 2)}m', + fontdict={'size': word_size, 'color': self.len_color[0], 'family': 'SimHei'}, rotation=rota2) # 标出距离 + if (self.all_axes == 1 or self.sho_axes_r == 1) and label1 == '短轴': + ax.text(center[0] + l1 * (vertices[0][0] / 2 - vertices[1][0] / 2), + center[1] + l1 * (vertices[0][1] / 2 - vertices[1][1] / 2), + center[2] + lz * (vertices[0][2] / 2 - vertices[1][2] / 2), + label1 + lb1 + f'{round(len_1, 2)}m', + fontdict={'size': word_size, 'color': self.sho_color[0], 'family': 'SimHei'}, + rotation=rota1) # 标出距离 + ax.text(center[0] + l2 * (-vertices[0][0] / 2 + vertices[1][0] / 2), + center[1] + l2 * (-vertices[0][1] / 2 + vertices[1][1] / 2), + center[2] + lz * (-vertices[0][2] / 2 + vertices[1][2] / 2), + label1 + lb2 + f'{round(len_2, 2)}m', + fontdict={'size': word_size, 'color': self.sho_color[1], 'family': 'SimHei'}, + rotation=rota1) # 标出距离 + elif (self.all_axes == 1 or self.sho_axes_r == 1) and label2 == '短轴': + ax.text(center[0] + l3 * (vertices[2][0] / 2 - vertices[1][0] / 2), + center[1] + l3 * (vertices[2][1] / 2 - vertices[1][1] / 2), + center[2] + lz * (vertices[2][2] / 2 - vertices[1][2] / 2), + label2 + lb3 + f'{round(len_3, 2)}m', + fontdict={'size': word_size, 'color': self.sho_color[0], 'family': 'SimHei'}, + rotation=rota2) # 标出距离 + ax.text(center[0] + l4 * (-vertices[2][0] / 2 + vertices[1][0] / 2), + center[1] + l4 * (-vertices[2][1] / 2 + vertices[1][1] / 2), + center[2] + lz * (-vertices[2][2] / 2 + vertices[1][2] / 2), + label2 + lb4 + f'{round(len_4, 2)}m', + fontdict={'size': word_size, 'color': self.sho_color[1], 'family': 'SimHei'}, + rotation=rota2) # 标出距离 + len_5 = np.abs(np.dot(axes[2], vertices[4]-center)) + len_6 = np.abs(np.dot(axes[2], vertices[0]-center)) + if self.all_axes == 1 or self.ver_axes_up == 1: + ax.text(center[0] + 6 / 5 * (vertices[4][0] / 2 - vertices[0][0] / 2), + center[1] + 6 / 5 * (vertices[4][1] / 2 - vertices[0][1] / 2), + center[2] + 6 / 5 * (vertices[4][2] / 2 - vertices[0][2] / 2), + '纵轴' + '上半轴' + f'{round(len_5, 2)}m', + fontdict={'size': word_size, 'color': self.ove_color[0], 'family': 'SimHei'}, + rotation=rota2) # 标出距离 + ax.text(center[0] + 6 / 5 * (-vertices[4][0] / 2 + vertices[0][0] / 2), + center[1] + 6 / 5 * (-vertices[4][1] / 2 + vertices[0][1] / 2), + center[2] + 6 / 5 * (-vertices[4][2] / 2 + vertices[0][2] / 2), + '纵轴下半轴' + f'{round(len_6, 2)}m', + fontdict={'size': word_size, 'color': self.ove_color[1], 'family': 'SimHei'}, + rotation=rota2) # 标出距离 + + +if __name__ == '__main__': + app = QApplication([]) + stats = MainWindow() + stats.ui.show() + app.exec() diff --git a/QT/3d_plot_2.ui b/QT/3d_plot_2.ui new file mode 100644 index 0000000..5289af9 --- /dev/null +++ b/QT/3d_plot_2.ui @@ -0,0 +1,385 @@ + + + Form + + + + 0 + 0 + 790 + 757 + + + + Form + + + + + + + + + + 请导入数据文件 + + + + + + + 选择文件 + + + + + + + 请导入井文件 + + + + + + + 选择文件 + + + + + + + + + + + levels: + + + + + + + 4 + + + 100.000000000000000 + + + 1.000000000000000 + + + 10.000000000000000 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + colorbar选择 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 包围盒 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 轴字体 + + + + + + + 8 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 轴长 + + + + + + + + + + + 密度图/散点图 + + + + + + + 长轴 + + + + + + + 长轴颜色 + + + + + + + 短轴 + + + + + + + 短轴颜色 + + + + + + + 纵轴 + + + + + + + 纵轴颜色 + + + + + + + + + + + 油井颜色选择 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 水井颜色选择 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 显示水井 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 显示油井 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 长轴视图 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 短轴视图 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 俯视图 + + + + + + + 清除数据 + + + + + + + + + + + + + + + diff --git a/__pycache__/creat_3d.cpython-310.pyc b/__pycache__/creat_3d.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..04bf04279e2874ac922b6a20d78c2d49b839d164 GIT binary patch literal 4217 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 XMe7L_go;px2Y~Eh)0cEp-+laF5}pcN literal 0 HcmV?d00001 diff --git a/creat_3d.py b/creat_3d.py new file mode 100644 index 0000000..7562e62 --- /dev/null +++ b/creat_3d.py @@ -0,0 +1,105 @@ +import matplotlib.pyplot as plt +from scipy.stats import gaussian_kde +from scipy.spatial.distance import cdist +import copy +from mpl_toolkits.mplot3d.art3d import Poly3DCollection +from matplotlib.colors import Normalize +import numpy as np +from skimage import measure +import matplotlib.cm as cm + +class create(): + def __init__(self): + """ + data is the x and y coordinate of data.pos + contours is the edge points + f is the kernel density value + levels is the levels for contours + x_range is the range of x label + y_range is the range of y label + + """ + self.data = [] + self.vertices = [] + self.faces = [] + self.levels = [] + self.x_range = [] + self.y_range = [] + + def data_pre(self, data_name): + with open(data_name, 'r') as f: + lines = f.readlines() + L_en=len(lines) + lines= lines[1:L_en] + data = [] + for line in lines: + x, y,z,t = line.strip().split("\t") + data.append(list(map(float, [x,y,z]))) + data = np.array(data) + self.data = data + self.x_range = [min(data[:, 0]), max(data[:,0])] + self.y_range = [min(data[:, 1]), max(data[:,1])] + self.z_range = [min(data[:, 2]), max(data[:,2])] + + def contours_pre(self, level): + + x = self.data[:, 0] + y = self.data[:, 1] + z = self.data[:, 2] + # 使用scipy库中的gaussian_kde函数计算密度估计 + k = gaussian_kde(self.data.T) + xi, yi, zi = np.mgrid[x.min()*1.5:x.max()*1.5:30j, y.min()*1.5:y.max()*1.5:30j, z.min()-50:z.max()+50:50j] + density = k(np.vstack([xi.flatten(), yi.flatten(), zi.flatten()])) + self.density =density + level = density.max()*level/100 + + # 使用 marching_cubes 生成等值面顶点和面 + verts, faces, _, _ = measure.marching_cubes(density.reshape(xi.shape), level=level) + a_0 = (x.max() - x.min()) / (verts[:, 0].max() - verts[:, 0].min()) + vertices_0 = (verts[:, 0] - verts[:, 0].min()) * a_0 + x.min()-5 + a_1 = (y.max() - y.min()+10) / (verts[:, 1].max() - verts[:, 1].min()) + vertices_1 = (verts[:, 1] - verts[:, 1].min()) * a_1 + y.min()-5 + a_2 = (z.max() - z.min()+10) / (verts[:, 2].max() - verts[:, 2].min()) + vertices_2 = (verts[:, 2] - verts[:, 2].min()) * a_2 + z.min()-5 + vertices = np.array([vertices_0, vertices_1, vertices_2]).T + self.vertices = vertices + self.faces = faces + + +class well_to_edge(): + def __init__(self): + """ + name is used to store the well names + type is the types of the wells + position is the coordinates of wells + min_distance is the minimum distances between wells and edge + welltoedge_points is the points responding to the min_distance + angle is the angles between the shortest distance direction vector from the well to the edge and the positive direction of the y-axis during clockwise rotation; + wells_num: the number of wells + """ + self.name = [] + self.type = [] + self.position = [] + self.min_distance = [] + self.welltoedge_points = [] + self.angle = [] + self.wells_num = 0 + + def wells_name_and_position(self, wells_name): + # 读取井位信息 + with open(wells_name, 'r') as f_j: + j_ing = f_j.readlines() + points = [] + typee = [] + namee = [] + for line in j_ing: + if ('0' or '1') in line: + name, x, y, z, type = line.strip().split("\t") + if name != 'name': + points.append(list(map(float, [x, y, z]))) + typee.append(list(map(int, [type]))) + namee.append(name.split('\n')) + self.position = points + self.name = namee + self.type = typee + self.wells_num = len(points)