Source code for shape_generator.shape_generator_holding

import math
import re

import numpy as np

from .helpers import channel_end, Circle
from .shape_generator import CrossSection


[docs]class CrossSectionHolding(CrossSection): """ cross section class for Holding Graz Attributes: add_dim (bool): add the dimension (height x width) to the label and output filename add_dn (bool): add the channel diameter (DN ...) to the label and output filename """
[docs] def __init__(self, label, description=None, add_dim=False, add_dn=None, **kwargs): """Initialise the cross-section class Args: label (str): name/label/number of the cross-section description (Optional[str]): optional description of the cross-section add_dim (bool): if the dimensions should be added to :py:attr:`~out_filename` used for the export add_dn (Optional[float]): if the channel dimension should be added to :py:attr:`~out_filename` used for the export enter the diameter as float **kwargs (object): see :py:attr:`~__init__` Keyword Args: description (Optional[str]): optional description of the cross section height (float): absolute height of the CS width (Optional[float]): absolute width of the CS (optional) can be calculated working_directory (str): directory where the files get saved unit (Optional[str]): enter unit to add the unit in the plots """ if isinstance(label, float): label = f'{label:0.0f}' else: label = str(label) label = label.strip('Pr_') self.add_dim = add_dim self.add_dn = add_dn self.description = description CrossSection.__init__(self, label, **kwargs)
def __repr__(self): return f'CrossSectionHolding({self})' @property def identifier(self): s = 'Pr_' + self.label if self.add_dim: s += f'+{self.height:0.0f}' if self.width: s += f'x{self.width:0.0f}' if self.add_dn: s += f'+DN{self.add_dn:0.0f}' return s def name_string(self): return self.identifier.replace('+', ' | ') def title_string(self): s = CrossSection.title_string(self) if (self.description is not None) and (self.label != self.description): s += f': {str(self.description).strip()}' return s ####################################################################################################################
[docs] @classmethod def standard(cls, label, description=None, height=np.NaN, width=None, r_channel=None, r_roof=None, r_wall=None, slope_bench=None, r_round=None, r_wall_bottom=None, h_bench=None, pre_bench=None, w_channel=None, **kwargs): """ standard cross section Args: label (str): see :py:attr:`~__init__` description (str): see :py:attr:`~__init__` height (float): see :py:attr:`~__init__` width (float): see :py:attr:`~__init__` r_channel (float): radius of the dry-weather channel (=Trockenwetter Rinne) w_channel (float): half width of the channel, only in combination with ``r_channel`` active pre_bench (float): slope of the upper end of the channel in degree, only in combination with ``r_channel`` active r_round (float): radius of the rounding of the edges, only in combination with ``r_channel`` active h_bench (float): height where the bench begins, only in combination with ``r_channel`` active slope_bench (float): slope of the bench (=Berme) in degree, or slope of the rainwater-floor ( =Regenwetterrinne) r_roof (float): radius of the roof (=Decke) r_wall (float): radius of the sidewall (=Seitenwand), only in combination with ``r_roof`` active r_wall_bottom (float): radius of the bottom sidewall (=untere Seitenwand), only in combination with ``r_wall`` active **kwargs (object): see :py:attr:`~__init__` Keyword Args: working_directory (str): directory where the files get saved unit (Optional[str]): enter unit to add the unit in the plots Returns: CrossSectionHolding: standard cross section Examples: see :doc:`standard_cross_section` .. figure:: images/standard.gif :align: center :alt: standard cross section :figclass: align-center Standard cross section +---------+---------------------+ | english | deutsch | +=========+=====================+ | channel | Trockenwetter-Rinne | +---------+---------------------+ | roof | Firste/Decke | +---------+---------------------+ | wall | Seitenwand | +---------+---------------------+ | bench | Berme | +---------+---------------------+ """ # ------------------------------------------------ cross_section = cls(label=label, description=description, height=height, width=width, **kwargs) # ------------------------------------------------ # TW-Rinne if r_channel is not None: cross_section.add(Circle(r_channel, x_m=r_channel)) # ------------------------------------------------ if pre_bench is not None: cross_section.add(channel_end(r_channel, pre_bench)) if (h_bench is not None) or (slope_bench is None): cross_section.add(pre_bench, '°slope') if h_bench is not None: cross_section.add(h_bench) elif w_channel is not None: cross_section.add(None, w_channel) else: if h_bench is not None: cross_section.add(h_bench) else: cross_section.add(r_channel) if r_round is None: r_round = 0 cross_section.add(r_channel + r_round, r_channel) # ------------------------------------------------ if slope_bench is not None: # Berme winkel in ° cross_section.add(slope_bench, '°slope') # ------------------------------------------------ if (r_channel is None) and (slope_bench is None): cross_section.add(0, width / 2) # ------------------------------------------------ if r_roof is None: # eckige Decke cross_section.add(None, width / 2) cross_section.add(height, width / 2) else: if r_wall is None: cross_section.add(None, width / 2) cross_section.add(height - r_roof, width / 2) else: # ------------------------------------------------ h1 = math.sqrt((r_wall - r_roof) ** 2 - (r_wall - width / 2) ** 2) # h_middle = round(height - r_roof - h1, 8) h_middle = height - r_roof - h1 # ------------------------------------------------ if r_wall_bottom is None: cross_section.add(None, width / 2) cross_section.add(h_middle, width / 2) else: cross_section.add(Circle(r_wall_bottom, x_m=h_middle, y_m=width / 2 - r_wall_bottom)) cross_section.add(h_middle) # ------------------------------------------------ cross_section.add(Circle(r_wall, x_m=h_middle, y_m=width / 2 - r_wall)) cross_section.add(h_middle + h1 / (r_wall - r_roof) * r_wall) # ------------------------------------------------ cross_section.add(Circle(r_roof, x_m=height - r_roof)) # ------------------------------------------------ return cross_section
####################################################################################################################
[docs] @classmethod def box(cls, label, height, width, channel=None, bench=None, roof=None, rounding=0.0, **kwargs): """ pre defined box (=Kasten) cross section Args: label (str): see :py:attr:`~__init__` height (float): see :py:attr:`~__init__` width (float): see :py:attr:`~__init__` channel (Optional[float]): diameter of the dry weather channel bench (Optional[float]): bench (=Berme) - ``''``: flache Berme - ``'R'``: V-förmiges Profil - ``'H'``: Schräge Verschneidung roof (Optional[float]): roof (=Decke) - ``''``: gerade - ``'B'``: Bogen - ``'K'``: Kreis rounding (Optional[float]): rounding of the edges **kwargs (object): see :py:attr:`~__init__` Keyword Args: description (Optional[str]): optional longer name of the cross section working_directory (str): directory where the files get saved unit (Optional[str]): enter unit to add the unit in the plots Returns: CrossSectionHolding: pre defined box (=Kasten) cross section Examples: see :doc:`show_case-kasten` """ name = 'K' cross_section = cls(label=label, height=height, width=width, **kwargs) # ------------------------------------------------ if channel is not None: channel = float(channel) if bench is None: bench = '' if roof is None: roof = '' # ------------------------------------------------ if channel or bench: name += '.' bench = str(bench).strip() if isinstance(channel, float): name += f'{channel:0.0f}' # diameter to radius channel /= 2 cross_section.add(Circle(channel, x_m=channel)) if isinstance(bench, str): # '' | 'R' | 'H' if bench != '45': name += str(bench) if bench == 'R': cross_section.add(30, '%slope') cross_section.add(None, width / 2) elif bench == 'H': cross_section.add(channel_end(channel, 45)) cross_section.add(45, '°slope') cross_section.add(None, width / 2) elif bench == '45': cross_section.add(channel_end(channel, 45)) cross_section.add(45, '°slope') cross_section.add(channel + rounding) cross_section.add(channel + rounding, width / 2) else: # Berme # cross_section.add(channel, width / 2) # cross_section.add(channel + rounding, width / 2) # --------either this cross_section.add(channel_end(channel, 45)) cross_section.add(45, '°slope') # --------or this # cross_section.add(channel, channel) # -------- cross_section.add(channel + rounding, None) cross_section.add(5, '°slope') cross_section.add(None, width / 2) else: # ebene Sohle cross_section.add(0, width / 2) # ------------------------------------------------ if roof: # '' | 'B' | 'K' name += '_' + str(roof) if roof == '': # gerade Decke cross_section.add(height, width / 2) elif roof == 'B': # Bogen-Decke cross_section.add(height - width * (1 - math.cos(math.radians(30))), width / 2) cross_section.add(Circle(width, x_m=height - width)) elif roof == 'K': # Kreis Decke cross_section.add(height - width / 2, width / 2) cross_section.add(Circle(width / 2, x_m=height - width / 2)) else: # gerade Decke cross_section.add(height, width / 2) # ------------------------------------------------ if cross_section.label is None or cross_section.label == '': cross_section.label = name return cross_section
####################################################################################################################
[docs] @classmethod def box_from_string(cls, label, **kwargs): """ create pre defined box (=Kasten) cross section with the string label. This function takes the information from the label and pass them to the :py:attr:`~box` - function. Args: label (str): see the :doc:`show_case-kasten` **kwargs (object): see :py:attr:`~__init__` Keyword Args: height (float): see :py:attr:`~__init__` width (float): see :py:attr:`~__init__` rounding (Optional[float]): rounding of the edges description (Optional[str]): optional longer name of the cross section working_directory (str): directory where the files get saved unit (Optional[str]): enter unit to add the unit in the plots Returns: CrossSectionHolding: pre defined box (=Kasten) cross section Examples: see :doc:`show_case-kasten` .. figure:: images/Kasten-Profile.gif :align: center :alt: Kasten-Profile :figclass: align-center Kasten-Profile """ infos = re.findall(r'(K)(\.?)(\d*)([RH]?)_?([BK]?)', label) # _(\d+)x(\d+) # 'Pr_K.30K' == 'Pr_K.30_K' if len(infos) == 1: infos = infos[0] _, _, channel, bench, roof = infos if channel != '': channel = float(channel) else: channel = None bench, roof = [x_ if x_ != '' else None for x_ in (bench, roof)] cross_section = cls.box(label, channel=channel, bench=bench, roof=roof, **kwargs) return cross_section # -------------------------------------- else: raise NotImplementedError(f'"{label}" unknown !')
[docs] @classmethod def from_point_cloud(cls, relative_coordinates, *args, **kwargs): """ get the cross sections from a point cloud where every point is relative to the lowers point in the cross section Args: relative_coordinates (list[tuple[float, float]] | numpy.array): list of height- and width tuple with the origin in the lowest point of the cross section *args: arguments, see :py:attr:`~__init__` **kwargs: keyword arguments, see :py:attr:`~__init__` Keyword Args: label (str): main name/label/number of the cross section description (Optional[str]): optional longer name of the cross section height (float): absolute height of the CS width (Optional[float]): absolute width of the CS (optional) can be calculated working_directory (str): directory where the files get saved unit (Optional[str]): enter unit to add the unit in the plots add_dim (bool): if the dimensions should be added to :py:attr:`~out_filename` used for the export add_dn (Optional[float]): if the channel dimension should be added to :py:attr:`~out_filename` used for the export enter the diameter as float Returns: CrossSectionHolding: of the point cloud .. figure:: images/point_cloud.gif :align: center :alt: point cloud :figclass: align-center Point cloud """ if isinstance(relative_coordinates, (list, tuple)): x, y = zip(*relative_coordinates) x = np.array(x) y = np.array(y) elif isinstance(relative_coordinates, np.ndarray): x = relative_coordinates[:, 0] y = relative_coordinates[:, 1] else: raise NotImplementedError() height = max(y) width = max(x) - min(x) kwargs.update({'height': height, 'width': width}) cross_section = cls(*args, **kwargs) # for the interpolation y = np.append(y, [0]) x = np.append(x, [0]) yi = sorted(set(y)) xi = np.interp(yi, y[:np.argmax(y) + 1], x[:np.argmax(y) + 1]) - \ np.interp(yi, y[::-1][:np.argmax(y[::-1]) + 1], x[::-1][:np.argmax(y[::-1]) + 1]) xi /= 2 for x, y in zip(yi, xi): if y == 0 and x in (0, height): continue cross_section.add(x, y) return cross_section