Coverage for bim2sim/utilities/visualize_spaces.py: 0%

61 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-12 17:09 +0000

1""" 

2Thermal Zone Visualization Module 

3================================= 

4 

5This module provides functionality to visualize thermal zones and save the 

6output as an image. It includes functions to convert RGB values to 

7OCC-compatible colors and to generate visualizations of thermal zones grouped 

8by various criteria. 

9 

10Example: 

11 For an example, please see e4_visualize_zone_binding.py in PluginTEASER. 

12 

13Functions: 

14 rgb_color(rgb): Returns an OCC viewer compatible color quantity based on 

15 r,g,b values. 

16 visualize_zones(zone_dict, folder_structure): Visualizes the thermal 

17 zones and saves the picture as a .png. 

18 

19Notes: 

20 Any additional information about the module, its purpose, and its usage 

21 can be included here. 

22""" 

23import logging 

24from pathlib import Path 

25from typing import Union 

26 

27import numpy as np 

28import ifcopenshell.geom 

29from OCC.Display.SimpleGui import init_display 

30from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB 

31from PIL import Image, ImageDraw 

32 

33from bim2sim.elements.bps_elements import ThermalZone 

34 

35logger = logging.getLogger(__name__) 

36 

37 

38def rgb_color(rgb) -> Quantity_Color: 

39 """Returns a OCC viewer compatible color quantity based on r,g,b values. 

40 

41 Args: 

42 rgb: must be a tuple with 3 values [0,1]. e.g. (0, 0.5, 0.7) 

43 

44 Returns: 

45 Quantity_Color object which is compatible with with the OCC viewer. 

46 """ 

47 return Quantity_Color(rgb[0], rgb[1], rgb[2], Quantity_TOC_RGB) 

48 

49 

50def visualize_zones( 

51 thermal_zones: list[ThermalZone], 

52 path: Path, 

53 filename: Union[str, None] = None): 

54 """Visualizes the ThermalZone element entities and saves the picture as 

55 a .png. Fetches the ThermalZone which are grouped before and creates an 

56 abstract building image, where each grouped zone has its own color. 

57 Afterwards, a legend is added with zone names and corresponding colors. 

58 The file is exported as .png to the export folder. 

59 

60 Args: 

61 thermal_zones: list of ThermalZone and AggregatedThermalZone instances 

62 path: pathlib Path where image is exported to 

63 filename: str of filename 

64 

65 Returns: 

66 No return value, image is saved directly. 

67 """ 

68 settings = ifcopenshell.geom.settings() 

69 settings.set(settings.USE_PYTHON_OPENCASCADE, True) 

70 settings.set(settings.USE_WORLD_COORDS, True) 

71 settings.set(settings.EXCLUDE_SOLIDS_AND_SURFACES, False) 

72 settings.set(settings.INCLUDE_CURVES, True) 

73 

74 # Call init_display 

75 # TODO this messes with the logger, but method like below doesn't work 

76 # with open(os.devnull, 'w') as devnull: 

77 display, start_display, add_menu, add_function_to_menu = init_display( 

78 display_triedron=False, background_gradient_color1=3 * [255], 

79 background_gradient_color2=3 * [255], size=(1920, 1080)) 

80 

81 legend = {} 

82 num = 1 

83 for tz in thermal_zones: 

84 name = tz.name 

85 rgb_tuple = tuple((np.random.choice(range(256), size=3))) 

86 rgb_tuple_norm = tuple([round(x / 256, 2) for x in rgb_tuple]) 

87 if name in list(legend.keys()): 

88 name = name + ' ' + str(num) 

89 num += 1 

90 legend[name] = rgb_tuple 

91 color = rgb_color(rgb_tuple_norm) 

92 # handle AggregatedThermalZone 

93 if hasattr(tz, "elements"): 

94 zones = tz.elements 

95 for zone in zones: 

96 display.DisplayShape(zone.space_shape, update=True, 

97 color=color, transparency=0.5) 

98 # handle normal ThermalZone 

99 else: 

100 display.DisplayShape(tz.space_shape, update=True, 

101 color=color, transparency=0.5) 

102 sorted_legend = {} 

103 for k in sorted(legend, key=len, reverse=False): 

104 sorted_legend[k] = legend[k] 

105 

106 nr_zones = len(thermal_zones) 

107 if not filename: 

108 filename = f"zoning_visualization_{str(nr_zones)}_zones.png" 

109 

110 save_path = Path(path / filename) 

111 display.View.Dump(str(save_path)) 

112 

113 text_size = 25 

114 zone_image = Image.open(save_path) 

115 image_editable = ImageDraw.Draw(zone_image) 

116 zone_image_size_y = zone_image.size[1] 

117 

118 rec_size = 20 

119 space = 30 

120 buffer = 10 

121 

122 legend_height = len(sorted_legend) * (text_size + space / 3) + buffer 

123 x0 = 0 

124 rec_to_text_spacing = 10 

125 y0 = zone_image_size_y - legend_height 

126 

127 for zone_name, color in sorted_legend.items(): 

128 xy = [(x0 + rec_to_text_spacing, y0), 

129 (x0 + + rec_to_text_spacing + rec_size, y0 + rec_size)] 

130 image_editable.rectangle(xy, fill=color, outline=None, width=text_size) 

131 image_editable.text( 

132 (x0 + rec_to_text_spacing + rec_size + 10, y0), zone_name, 

133 (0, 0, 0)) 

134 y0 += space 

135 

136 zone_image.save(save_path) 

137 logger.info(f"Exported visualization of combined ThermalZone instances to " 

138 f"{save_path}.")