Files
old-krovovi-kalkulator/helix/Services/dxf_helper.py
2017-11-07 09:23:57 +01:00

1003 lines
44 KiB
Python

import math
from statistics import mean
from helix.calculators.subarray_helper import get_subarray_sizes_and_rows
from helix.constants.dxf_validation import INVALID_DUAL_TILT_DESIGN
from helix.constants.panel_type import PanelType
from helix.constants.subarray import SUBARRAY_SIZE_BIG
from helix.constants.system_type import SystemType
from helix.helpers.polygon_helper import *
from helix.models.coordinate import Coordinate
from helix.models.dxf.dxf_error import DXFError
from helix.models.dxf.graph_direction import GraphDirection
from helix.models.dxf.graph_node import GraphNode
from helix.models.dxf.graph_node_store import GraphNodeStore
from helix.models.dxf.polygon import Polygon
from helix.models.panel import Panel
from helix.validators.dxf_layer_validator import DXFLayerValidator
class DXFHelper(object):
def __init__(self):
self.aurora_detector = DXFLayerValidator()
def is_new_aurora_format(self):
""" Determine if it's the new dxf format
return
boolean
"""
return self.aurora_detector.is_new_aurora_format()
@staticmethod
def generate_panels(modules, translated_modules):
"""Joins the modules and creates
Parameters:
modules (list): List of obtained modules
translated_modules (list): A list containing the shifted
positions of the modules
Returns:
list
"""
panels = []
for idx, module in enumerate(modules):
x_center = mean([x for x, _ in module.points])
y_center = mean([y for _, y in module.points])
rotation = module.determine_orientation()
x_translated_center = mean([x for x, _ in translated_modules[idx].points])
y_translated_center = mean([y for _, y in translated_modules[idx].points])
panels.append(Panel(id=idx + 1, coordinate=Coordinate(
x_translated_center, y_translated_center, rotation), original_coordinate=Coordinate(x_center, y_center, rotation)))
return panels
@staticmethod
def should_consolidate_modules(modules, system_type, module_constants):
"""Determine if the modules should be consolidated into panel pairs.
Some dual tilt files came with separate modules, this method determines
if we should consolidate them.
Arguments:
modules (list) List of helix.models.dxf.Polygon
system_type (object) Type of system selected single tilt, dual tilt
module_constants (object)
helix.constants.module_type_constants.dual_tilt_96_cell_constants.DualTilt96CellConstants
Returns:
boolean
"""
if system_type != SystemType.dualTilt:
return False
for module in modules:
points = module.sorted_points()
p1 = points[0]
other_points = sorted(points[1:], key=lambda x: (x[0] - p1[0]) ** 2 + (x[1] - p1[1]) ** 2)
p2 = other_points[1]
if (math.hypot(p2[0] - p1[0], p2[1] - p1[1]) - min(module_constants.panel_spacing)) < 1e-3:
return True
return False
@staticmethod
def consolidate_dual_tilt_modules(original_modules, system_type,
pair_spacing):
"""Consolidate the dual tilt modules. This method is called
with a certain type of dual tilt files that had the modules
separated
Parameters:
original_modules (list) Contains the list of modules from the dxf
file
system_type (object) Type of system selected single tilt, dual tilt
pair_spacing (object) Contains the possible pair spacing that a
dual tilt system will have
"""
if system_type == SystemType.dualTilt:
modules = []
while len(original_modules) > 0:
current_module = original_modules.pop(0)
modules.append(current_module)
found_pair = False
for idx, potential_pair in enumerate(original_modules):
if current_module.shares_module_on_long_edge(potential_pair,
pair_spacing):
current_module.consolidate_with(potential_pair,
pair_spacing)
original_modules.pop(idx)
found_pair = True
break
if not found_pair:
raise DXFError(INVALID_DUAL_TILT_DESIGN)
else:
return original_modules
return modules
def build_polygons(self, dxf_entities):
"""Obtain two types of polygon objects,
that represent modules and buildings.
It's important to understand that the
polygons are scaled using inches.
Arguments:
dxf_entities (list) This list contains
dxfgrabber.entities
"""
building_lines = []
module_lines = []
for entity in dxf_entities:
self.aurora_detector.add_layer(entity.layer)
if entity.layer == 'Modules': # is a module
module_lines.append(entity)
elif entity.layer == 'Roofs' or entity.layer == 'Buildings': # is a building/roof line
building_lines.append(entity)
self.aurora_detector.determine_file()
inches_per_feet = 12
buildings = [p.scale(inches_per_feet, inches_per_feet) for p in DXFHelper.generate_polygons(building_lines)]
modules = [p.scale(inches_per_feet, inches_per_feet) for p in DXFHelper.generate_polygons(module_lines)]
return buildings, modules
@staticmethod
def generate_polygons(lines):
polygons = []
for line in lines:
if len(polygons) == 0 or not polygons[-1].continues_with_line(line):
polygons.append(Polygon(line))
elif line.end in polygons[-1].points:
continue
else:
polygons[-1].points.append(line.end)
return polygons
@staticmethod
def translate_towards_origin(buildings, modules):
"""Obtains the minimum value of x and y points and shifts
all the modules and buildings.
Arguments:
buildings (list): List of buildings
modules (list): List of modules
Returns:
tuple
"""
min_x = float('inf') # positive infinity
min_y = float('inf')
for polygon in buildings + modules:
min_x = min(min_x, min(x for x, _ in polygon.points))
min_y = min(min_y, min(y for _, y in polygon.points))
translated_buildings = [Polygon(points=[(x - min_x, y - min_y) for x, y in polygon.points]) for polygon in buildings]
translated_modules = [Polygon(points=[(x - min_x, y - min_y) for x, y in polygon.points]) for polygon in modules]
return translated_buildings, translated_modules
@staticmethod
def get_polygons_counterclockwise(polygons):
"""Checks if polygon's points are in counterclockwise order, if not reverse the order.
Arguments:
polygons (list): List of polygons
Returns:
list of polygons with points in counterclockwise order
"""
output_polygons = []
for polygon in polygons:
points = polygon.points
# checking if building's points are in clockwise order
cumulative = 0
for i in range(len(points)):
current = points[i - 1]
next = points[i]
cumulative += (next[0] - current[0]) * (next[1] + current[1])
clockwise = cumulative > 0
# change to counter-clockwise if necessary
# further code assumes we have counter-clockwise points for the building
if clockwise:
points = list(reversed(points))
output_polygons.append(Polygon(points=[(x, y) for x, y in points]))
return output_polygons
@staticmethod
def build_node_graph(panels, spacing):
nodes = []
node_store = GraphNodeStore()
for panel in panels:
x_spacing = spacing[0]
y_spacing = spacing[1]
node = GraphNode(panel, x_spacing, y_spacing)
nodes.append(node)
node_store.add_node(node)
for node in nodes:
if len(node.neighboring_nodes()) == 8:
continue
for x in (0, 1, -1):
for y in (0, 1, -1):
if x == y == 0:
continue
rotation = math.radians(node.coordinate.rotation)
x_spacing = (x * node.x_spacing * math.cos(rotation)) - (y * node.y_spacing * math.sin(rotation))
y_spacing = (x * node.x_spacing * math.sin(rotation)) + (y * node.y_spacing * math.cos(rotation))
coordinate = Coordinate(node.coordinate.x + x_spacing, node.coordinate.y + y_spacing, node.coordinate.rotation)
if coordinate.x < 0 or coordinate.y < 0:
continue
direction = GraphDirection((x, y))
if node.has_existing_neighbor(direction):
continue
neighbor = node_store.find_coordinate(coordinate)
if neighbor:
node.add_neighbor(neighbor, direction)
if len(node.neighboring_nodes()) == 0:
raise DXFError("Error - invalid module spacing. Please check to make sure the correct system type and panel spacing are present")
return nodes
@staticmethod
def detect_subarrays(nodes, panels):
subarray_number = 0
def walk_graph_and_assign_subarray(node, subarray_number):
if node.panel.subarray is not None:
return
node.panel.subarray = subarray_number
for neighbor in node.neighboring_nodes():
walk_graph_and_assign_subarray(neighbor, subarray_number)
for node in nodes:
if node.panel.subarray is None:
subarray_number += 1
try:
walk_graph_and_assign_subarray(node, subarray_number)
except RecursionError:
raise DXFError(SUBARRAY_SIZE_BIG)
panels.sort(key=lambda p: p.subarray)
subarray_list = get_subarray_sizes_and_rows(panels)
subarrays = {}
for subarray in subarray_list:
subarray_number = subarray.subarray_number
if not subarrays.get(subarray.subarray_number):
subarrays[subarray_number] = subarray
else:
subarrays[subarray_number].size += subarray.size
return list(subarrays.values())
@staticmethod
def detect_panel_types(nodes):
for node in nodes:
ordinal_neighbors = node.ordinal_neighbors()
ordinal_neighbors_count = len(ordinal_neighbors)
if ordinal_neighbors_count == 4:
node.panel.panel_type = PanelType.Middle
elif ordinal_neighbors_count <= 2:
node.panel.panel_type = PanelType.Corner
elif ordinal_neighbors.get(GraphDirection.North) is None or ordinal_neighbors.get(GraphDirection.South) is None:
node.panel.panel_type = PanelType.NorthSouth
elif ordinal_neighbors.get(GraphDirection.East) is None or ordinal_neighbors.get(GraphDirection.West) is None:
node.panel.panel_type = PanelType.EastWest
else:
raise DXFError("Invalid Array")
pass
@staticmethod
def detect_wind_zones(panels, buildings, modules, l_b, system_type):
if system_type == SystemType.dualTilt:
DXFHelper.__detect_dual_tilt_wind_zones__(panels, buildings, modules, l_b)
else:
DXFHelper.__detect_single_tilt_wind_zones__(panels, buildings, modules, l_b)
@staticmethod
def __detect_dual_tilt_wind_zones__(panels, buildings, modules, l_b):
sorted_panels = sorted(panels, key=lambda p: p.id)
wind_zone_a = DXFHelper.__generate_wind_zone_a_dual_tilt__(buildings, l_b)
fuzzy_wind_zone_a = DXFHelper.__generate_wind_zone_a_fuzzy_dual_tilt__(buildings, l_b)
wind_zone_b = DXFHelper.__generate_wind_zone_b_dual_tilt__(buildings, l_b)
fuzzy_wind_zone_b = DXFHelper.__generate_wind_zone_b_fuzzy_dual_tilt__(buildings, l_b)
wind_zone_c = DXFHelper.__generate_wind_zone_c_dual_tilt__(buildings, l_b)
fuzzy_wind_zone_c = DXFHelper.__generate_wind_zone_c_fuzzy_dual_tilt__(buildings, l_b)
wind_zone_d = DXFHelper.__generate_wind_zone_d_dual_tilt__(buildings, l_b)
fuzzy_wind_zone_d = DXFHelper.__generate_wind_zone_d_fuzzy_dual_tilt__(buildings, l_b)
for idx, panel in enumerate(sorted_panels):
module = modules[idx]
if DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_a):
panel.wind_zone = 0
elif DXFHelper.__is_panel_in_wind_zone__(module, fuzzy_wind_zone_a):
panel.wind_zone = 0
panel.fuzzy_wind_zone = True
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_b):
panel.wind_zone = 1
elif DXFHelper.__is_panel_in_wind_zone__(module, fuzzy_wind_zone_b):
panel.wind_zone = 1
panel.fuzzy_wind_zone = True
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_c):
panel.wind_zone = 2
elif DXFHelper.__is_panel_in_wind_zone__(module, fuzzy_wind_zone_c):
panel.wind_zone = 2
panel.fuzzy_wind_zone = True
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_d):
panel.wind_zone = 3
elif DXFHelper.__is_panel_in_wind_zone__(module, fuzzy_wind_zone_d):
panel.wind_zone = 3
panel.fuzzy_wind_zone = True
else: # wind zone E
panel.wind_zone = 4
@staticmethod
def __detect_single_tilt_wind_zones__(panels, buildings, modules, l_b):
sorted_panels = sorted(panels, key=lambda p: p.id)
panel_orientation = panels[0].coordinate.rotation
wind_zone_a = DXFHelper.__generate_wind_zone_a_single_tilt__(buildings, l_b, panel_orientation)
fuzzy_wind_zone_a = DXFHelper.__generate_wind_zone_a_fuzzy_single_tilt__(buildings, l_b)
wind_zone_b = DXFHelper.__generate_wind_zone_b_single_tilt__(buildings, l_b, panel_orientation)
fuzzy_wind_zone_b = DXFHelper.__generate_wind_zone_b_fuzzy_single_tilt__(buildings, l_b)
wind_zone_c = DXFHelper.__generate_wind_zone_c_single_tilt__(buildings, l_b, panel_orientation)
fuzzy_wind_zone_c = DXFHelper.__generate_wind_zone_c_fuzzy_single_tilt__(buildings, l_b)
wind_zone_d = DXFHelper.__generate_wind_zone_d_single_tilt__(buildings, l_b, panel_orientation)
fuzzy_wind_zone_d = DXFHelper.__generate_wind_zone_d_fuzzy_single_tilt__(buildings, l_b)
wind_zone_i = DXFHelper.__generate_wind_zone_i_single_tilt__(buildings, l_b, panel_orientation)
wind_zone_h = DXFHelper.__generate_wind_zone_h_single_tilt__(buildings, l_b, panel_orientation)
wind_zone_j = DXFHelper.__generate_wind_zone_j_single_tilt__(buildings, l_b, panel_orientation)
wind_zone_e = DXFHelper.__generate_wind_zone_e_single_tilt__(buildings, l_b, panel_orientation)
wind_zone_f = DXFHelper.__generate_wind_zone_f_single_tilt__(buildings, l_b, panel_orientation)
wind_zone_g = DXFHelper.__generate_wind_zone_g_single_tilt__(buildings, l_b, panel_orientation)
for idx, panel in enumerate(sorted_panels):
module = modules[idx]
if DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_a):
panel.wind_zone = 0
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_i):
panel.wind_zone = 8
elif DXFHelper.__is_panel_in_wind_zone__(module, fuzzy_wind_zone_a):
panel.wind_zone = 0
panel.fuzzy_wind_zone = True
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_j):
panel.wind_zone = 9
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_b):
panel.wind_zone = 1
elif DXFHelper.__is_panel_in_wind_zone__(module, fuzzy_wind_zone_b):
panel.wind_zone = 1
panel.fuzzy_wind_zone = True
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_h):
panel.wind_zone = 7
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_c):
panel.wind_zone = 2
elif DXFHelper.__is_panel_in_wind_zone__(module, fuzzy_wind_zone_c):
panel.wind_zone = 2
panel.fuzzy_wind_zone = True
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_e):
panel.wind_zone = 4
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_f):
panel.wind_zone = 5
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_d):
panel.wind_zone = 3
elif DXFHelper.__is_panel_in_wind_zone__(module, fuzzy_wind_zone_d):
panel.wind_zone = 3
panel.fuzzy_wind_zone = True
elif DXFHelper.__is_panel_in_wind_zone__(module, wind_zone_g):
panel.wind_zone = 6
else:
panel.wind_zone = 10
@staticmethod
def __is_panel_in_wind_zone__(module, wind_zone):
for x, y in module.points:
for wind_subzone in wind_zone:
if point_inside_polygon(x, y, wind_subzone):
return True
return False
@staticmethod
def __compute_segment_direction(p1, p2):
"""
Computes direction of a segment. Points taken from building outline are assumed to be in counterclockwise order.
:param p1: first point
:param p2: second point
:return: tuple representing orientation ('north', 'south', 'east', 'west') and variation in degrees from ideally
directed segment
"""
segment_angle = (math.degrees(math.atan2(p1[1] - p2[1], p1[0] - p2[0])) + 360) % 360
if segment_angle >= 315 or segment_angle < 45:
return 'north', segment_angle if segment_angle < 45 else 360 - segment_angle
elif 45 <= segment_angle < 135:
return 'west', abs(90 - segment_angle)
elif 135 <= segment_angle < 225:
return 'south', abs(180 - segment_angle)
else:
return 'east', abs(270 - segment_angle)
@staticmethod
def compute_corner_directions(vertex, prev, next, angle_correction):
"""
Determines if point is located in north/east corner
:param vertex: point located in the corner
:param prev: point previous to vertex, assuming counterclockwise order
:param next: point next to vertex, assuming counterclockwise order
:param angle_correction: points are rotated by this angle first, in degrees
:return: (is_north, is_east) tuple
"""
rotated = DXFHelper.__rotate_points([vertex, prev, next], math.radians(angle_correction))
vertex_rotated, prev_rotated, next_rotated = rotated[0], rotated[1], rotated[2]
prev_segment_orientation, prev_dev = DXFHelper.__compute_segment_direction(prev_rotated, vertex_rotated)
next_segment_orientation, next_dev = DXFHelper.__compute_segment_direction(vertex_rotated, next_rotated)
is_north = prev_segment_orientation == 'north' or next_segment_orientation == 'north'
dirs = (prev_segment_orientation, next_segment_orientation)
if 'north' in dirs and 'south' not in dirs:
is_north = True
elif 'south' in dirs and 'north' not in dirs:
is_north = False
elif dirs == ('north', 'south'):
is_north = prev_dev < next_dev
elif dirs == ('south', 'north'):
is_north = next_dev < prev_dev
elif dirs == ('east', 'west'):
is_north = True
elif dirs == ('west', 'east'):
is_north = False
elif dirs == ('west', 'west') or dirs == ('east', 'east'):
is_north = prev_rotated[0] > next_rotated[0]
if 'east' in dirs and 'west' not in dirs:
is_east = True
elif 'west' in dirs and 'east' not in dirs:
is_east = False
elif dirs == ('east', 'west'):
is_east = prev_dev < next_dev
elif dirs == ('west', 'east'):
is_east = next_dev < prev_dev
elif dirs == ('north', 'south'):
is_east = False
elif dirs == ('south', 'north'):
is_east = True
elif dirs == ('north', 'north') or dirs == ('south', 'south'):
is_east = prev_rotated[1] < next_rotated[1]
return is_north, is_east
@staticmethod
def __rotate_points(points, angle):
"""
:param points: points to be rotated as list of tuples
:param angle: angle in radians
:return: list of rotated points
"""
sin = math.sin(angle)
cos = math.cos(angle)
return [(x * cos - y * sin, x * sin + y * cos) for x, y in points]
@staticmethod
def __rotate_point(point, angle):
return DXFHelper.__rotate_points([point], angle)[0]
@staticmethod
def __generate_wind_zone__(buildings, scaling_factor, points_callback, panel_orientation):
"""
Important: polygons representing buildings are expected to have points in counterclockwise order
"""
wind_zones = []
for building in buildings:
for idx, vertex in enumerate(building.points):
prev = building.points[(idx - 1) % len(building.points)]
next = building.points[(idx + 1) % len(building.points)]
a_sq = (vertex[0] - prev[0]) ** 2 + (vertex[1] - prev[1]) ** 2
b_sq = (vertex[0] - next[0]) ** 2 + (vertex[1] - next[1]) ** 2
c_sq = (next[0] - prev[0]) ** 2 + (next[1] - prev[1]) ** 2
distance_to_prev = math.sqrt(a_sq)
distance_to_next = math.sqrt(b_sq)
angle = math.acos((c_sq - a_sq - b_sq) / (-2 * distance_to_prev * distance_to_next))
# angles between x-axis and line created by current and next/previous vertex
angle_to_next = math.atan2(next[1] - vertex[1], next[0] - vertex[0])
angle_to_prev = math.atan2(prev[1] - vertex[1], prev[0] - vertex[0])
is_north, is_east = DXFHelper.compute_corner_directions(vertex, prev, next, -panel_orientation)
relative_angle = ((angle_to_next - angle_to_prev + 2 * math.pi) % (2 * math.pi)) - math.pi
if abs(angle_to_next - math.radians(panel_orientation)) > abs(angle_to_prev - math.radians(panel_orientation)):
max_distance = distance_to_next
else:
max_distance = distance_to_prev
orientation = angle_to_next
def generate_point(x, y):
d_x, d_y = DXFHelper.__rotate_point((x, y), orientation)
return vertex[0] + d_x, vertex[1] + d_y
if angle <= math.radians(135) and relative_angle >= 0:
points = points_callback(angle, is_north, is_east, max_distance)
if points:
wind_zones.append([generate_point(scaling_factor * x, scaling_factor * y) for x, y in points])
return wind_zones
@staticmethod
def __generate_dual_tilt_wind_zone_with_transforms__(buildings, scaling_factor, points):
def callback(angle, *_):
if angle < math.radians(80):
return []
rotation = angle - math.radians(90)
def inner_transform(x, y, perform_rotation, rotation_value=rotation):
if perform_rotation:
return DXFHelper.__rotate_point((x, y), rotation_value)
else:
return x, y
transformed_points = []
for x, y, apply_rotation, rotation_scale in points:
if rotation_scale is not None:
rotation_value = rotation_scale * rotation
else:
rotation_value = rotation
transformed_points.append(inner_transform(x, y, apply_rotation, rotation_value))
return transformed_points
return DXFHelper.__generate_wind_zone__(buildings, scaling_factor, callback, 0)
@staticmethod
def __generate_wind_zone_a_dual_tilt__(buildings, scaling_factor):
wind_zone_points = [
(0, 0, False, None),
(2, 0, False, None),
(2, 1, False, None),
(1, 1, True, 0.5),
(1, 2, True, None),
(0, 2, True, None)
]
return DXFHelper.__generate_dual_tilt_wind_zone_with_transforms__(buildings, scaling_factor, wind_zone_points)
@staticmethod
def __generate_wind_zone_b_dual_tilt__(buildings, scaling_factor):
wind_zone_points = [
(1, 1, True, 0.5),
(2, 1, False, None),
(2, 0, False, None),
(2, 0, False, None),
(4, 0, False, None),
(4, 2, False, None),
(2, 2, True, 0.5),
(2, 4, True, None),
(0, 4, True, None),
(0, 2, True, None),
(1, 2, True, None),
]
return DXFHelper.__generate_dual_tilt_wind_zone_with_transforms__(buildings, scaling_factor, wind_zone_points)
@staticmethod
def __generate_wind_zone_c_dual_tilt__(buildings, scaling_factor):
wind_zone_points = [
(2, 2, True, 0.5),
(4, 2, False, None),
(4, 0, False, None),
(6, 0, False, None),
(6, 3, False, None),
(3, 3, True, 0.5),
(3, 6, True, None),
(0, 6, True, None),
(0, 4, True, None),
(2, 4, True, None),
]
return DXFHelper.__generate_dual_tilt_wind_zone_with_transforms__(buildings, scaling_factor, wind_zone_points)
@staticmethod
def __generate_wind_zone_d_dual_tilt__(buildings, scaling_factor):
wind_zone_points = [
(3, 3, True, 0.5),
(6, 3, False, None),
(6, 0, False, None),
(8, 0, False, None),
(8, 4, False, None),
(4, 8, True, None),
(0, 8, True, None),
(0, 6, True, None),
(3, 6, True, None),
]
return DXFHelper.__generate_dual_tilt_wind_zone_with_transforms__(buildings, scaling_factor, wind_zone_points)
@staticmethod
def __generate_fuzzy_wind_zone_dual_tilt__(buildings, scaling_factor, inner_l_b, outer_l_b):
def callback(angle, *_):
if angle >= math.radians(80):
return []
number_points_on_inner_arc = int(math.ceil(inner_l_b * scaling_factor / 10))
number_points_on_outer_arc = int(math.ceil(outer_l_b * scaling_factor / 10))
inner_points_length = (inner_l_b, number_points_on_inner_arc)
outer_points_length = (outer_l_b, number_points_on_outer_arc)
points = []
if inner_l_b == 0:
points.append((0, 0))
for idx, (l_b_length, number_points) in enumerate((inner_points_length, outer_points_length)):
if number_points == 0:
continue
for step in range(number_points + 1):
sub_angle = (angle / number_points) * step
x = l_b_length * math.cos(sub_angle)
y = l_b_length * math.sin(sub_angle)
if idx == 0:
points.append((x, y))
else:
points.insert(0, (x, y))
return points
return DXFHelper.__generate_wind_zone__(buildings, scaling_factor, callback, 0)
@staticmethod
def __generate_wind_zone_a_fuzzy_dual_tilt__(buildings, scaling_factor):
return DXFHelper.__generate_fuzzy_wind_zone_dual_tilt__(buildings, scaling_factor, 0, math.sqrt(5))
@staticmethod
def __generate_wind_zone_b_fuzzy_dual_tilt__(buildings, scaling_factor):
return DXFHelper.__generate_fuzzy_wind_zone_dual_tilt__(buildings, scaling_factor, math.sqrt(5), math.sqrt(20))
@staticmethod
def __generate_wind_zone_c_fuzzy_dual_tilt__(buildings, scaling_factor):
return DXFHelper.__generate_fuzzy_wind_zone_dual_tilt__(buildings, scaling_factor, math.sqrt(20), math.sqrt(45))
@staticmethod
def __generate_wind_zone_d_fuzzy_dual_tilt__(buildings, scaling_factor):
return DXFHelper.__generate_fuzzy_wind_zone_dual_tilt__(buildings, scaling_factor, math.sqrt(45), math.sqrt(80))
# Single Tilt
@staticmethod
def __generate_single_tilt_wind_zone_with_transforms__(buildings, scaling_factor, northern_zone, points, panel_orientation):
def callback(angle, is_north, is_east, max_distance):
if is_north != northern_zone:
return []
if angle < math.radians(80):
return []
rotation = angle - math.radians(90)
def inner_transform(x, y, perform_rotation, rotation_value=rotation):
if y == -1:
y = max_distance / scaling_factor
if not is_east:
perform_rotation = not perform_rotation
# swap x,y coordinates - reflection about line x = y
new_x = y
new_y = x
x = new_x
y = new_y
if perform_rotation:
return DXFHelper.__rotate_point((x, y), rotation_value)
else:
return x, y
transformed_points = []
for x, y, apply_rotation, rotation_scale in points:
if rotation_scale is not None:
rotation_value = rotation_scale * rotation
else:
rotation_value = rotation
transformed_points.append(inner_transform(x, y, apply_rotation, rotation_value))
return transformed_points
return DXFHelper.__generate_wind_zone__(buildings, scaling_factor, callback, panel_orientation)
@staticmethod
def __generate_wind_zone_a_single_tilt__(buildings, scaling_factor, panel_orientation):
wind_zone_points = [
(0, 0, False, None),
(0, 2, True, None),
(2, 2, True, 0.5),
]
return DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings, scaling_factor, True,
wind_zone_points, panel_orientation)
@staticmethod
def __generate_wind_zone_b_single_tilt__(buildings, scaling_factor, panel_orientation):
first_wind_zone_points = [
(0, 0, False, None),
(2, 0, False, None),
(2, 2, True, 0.5),
]
first_wind_zone_polygons = DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings,
scaling_factor, True,
first_wind_zone_points,
panel_orientation)
second_wind_zone_points = [
(0, 2, True, None),
(0, 4, True, None),
(1, 4, True, None),
(2, 3, True, None),
(2, 2, True, 0.5),
]
second_wind_zone_polygons = DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings,
scaling_factor, True,
second_wind_zone_points,
panel_orientation)
return first_wind_zone_polygons + second_wind_zone_polygons
@staticmethod
def __generate_wind_zone_c_single_tilt__(buildings, scaling_factor, panel_orientation):
wind_zone_points = [
(2, 0, False, None),
(4, 0, False, None),
(4, 4, True, 0.5),
(2, 6, True, None),
(0, 6, True, None),
(0, 4, True, None),
(1, 4, True, None),
(2, 3, True, None),
]
return DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings, scaling_factor, True,
wind_zone_points, panel_orientation)
@staticmethod
def __generate_wind_zone_d_single_tilt__(buildings, scaling_factor, panel_orientation):
wind_zone_points = [
(4, 0, False, None),
(6, 0, False, None),
(6, 6, True, 0.5),
(4, 8, True, None),
(0, 8, True, None),
(0, 6, True, None),
(2, 6, True, None),
(2, 6, True, None),
(4, 4, True, 0.5),
]
return DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings, scaling_factor, True,
wind_zone_points, panel_orientation)
@staticmethod
def __generate_wind_zone_i_single_tilt__(buildings, scaling_factor, panel_orientation):
wind_zone_points = [
(0, 0, False, None),
(0, 2, True, None),
(1.5, 2, True, 2.0 / 3.0),
(1, 0, False, None),
]
return DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings, scaling_factor, False,
wind_zone_points, panel_orientation)
@staticmethod
def __generate_wind_zone_h_single_tilt__(buildings, scaling_factor, panel_orientation):
wind_zone_points = [
(0, 2, True, None),
(0, 4, True, None),
(2, 4, True, 2.0 / 3.0),
(1.5, 2, True, 2.0 / 3.0),
]
return DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings, scaling_factor, False,
wind_zone_points, panel_orientation)
@staticmethod
def __generate_wind_zone_j_single_tilt__(buildings, scaling_factor, panel_orientation):
wind_zone_points = [
(1, 0, False, None),
(3, 0, False, None),
(3, 2, True, 2.0 / 3.0),
(1.5, 2, True, 2.0 / 3.0),
]
return DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings, scaling_factor, False,
wind_zone_points, panel_orientation)
@staticmethod
def __generate_wind_zone_e_single_tilt__(buildings, scaling_factor, panel_orientation):
wind_zone_points = [
(0, 4, True, None),
(0, 7, True, None),
(3, 7, True, 2.0 / 3.0),
(2, 4, True, 2.0 / 3.0),
]
return DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings, scaling_factor, False,
wind_zone_points, panel_orientation)
@staticmethod
def __generate_wind_zone_f_single_tilt__(buildings, scaling_factor, panel_orientation):
wind_zone_points = [
(3, 0, False, None),
(8, 0, False, None),
(8, 2, False, None),
(3, 7, True, 2.0 / 3.0),
(2, 4, True, 2.0 / 3.0),
(1.5, 2, True, 2.0 / 3.0),
(3, 2, True, 2.0 / 3.0),
]
return DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings, scaling_factor, False,
wind_zone_points, panel_orientation)
@staticmethod
def __generate_wind_zone_g_single_tilt__(buildings, scaling_factor, panel_orientation):
wind_zone_points = [
(0, 7, True, None),
(0, -1, True, None),
(4, -1, True, None),
(4, 6, True, 2.0 / 3.0),
(3, 7, True, 2.0 / 3.0),
]
return DXFHelper.__generate_single_tilt_wind_zone_with_transforms__(buildings, scaling_factor, False,
wind_zone_points, panel_orientation)
@staticmethod
def __generate_fuzzy_wind_zone_single_tilt__(buildings, scaling_factor, inner_l_b, outer_l_b):
def callback(angle, *_):
if angle >= math.radians(80):
return []
number_points_on_inner_arc = int(math.ceil(inner_l_b * scaling_factor / 10))
number_points_on_outer_arc = int(math.ceil(outer_l_b * scaling_factor / 10))
inner_points_length = (inner_l_b, number_points_on_inner_arc)
outer_points_length = (outer_l_b, number_points_on_outer_arc)
points = []
if inner_l_b == 0:
points.append((0, 0))
for idx, (l_b_length, number_points) in enumerate((inner_points_length, outer_points_length)):
if number_points == 0:
continue
start = 0
end = number_points
for step in range(start, end + 1):
sub_angle = (angle / number_points) * step
x = l_b_length * math.cos(sub_angle)
y = l_b_length * math.sin(sub_angle)
if idx == 0:
points.append((x, y))
else:
points.insert(0, (x, y))
return points
return DXFHelper.__generate_wind_zone__(buildings, scaling_factor, callback, 0)
@staticmethod
def __generate_wind_zone_a_fuzzy_single_tilt__(buildings, scaling_factor):
return DXFHelper.__generate_fuzzy_wind_zone_single_tilt__(buildings, scaling_factor, 0, math.sqrt(8))
@staticmethod
def __generate_wind_zone_b_fuzzy_single_tilt__(buildings, scaling_factor):
return DXFHelper.__generate_fuzzy_wind_zone_single_tilt__(buildings, scaling_factor, math.sqrt(8), math.sqrt(20))
@staticmethod
def __generate_wind_zone_c_fuzzy_single_tilt__(buildings, scaling_factor):
return DXFHelper.__generate_fuzzy_wind_zone_single_tilt__(buildings, scaling_factor, math.sqrt(20), math.sqrt(44))
@staticmethod
def __generate_wind_zone_d_fuzzy_single_tilt__(buildings, scaling_factor):
return DXFHelper.__generate_fuzzy_wind_zone_single_tilt__(buildings, scaling_factor, math.sqrt(44),
math.sqrt(80))
@staticmethod
def l_b_polygons(buildings, scaling_factor, system_type, panel_orientation):
if system_type == SystemType.dualTilt:
return DXFHelper.__dual_tilt_wind_zone_polygons__(buildings, scaling_factor)
else:
return DXFHelper.__single_tilt_wind_zone_polygons__(buildings, scaling_factor, panel_orientation)
@staticmethod
def __dual_tilt_wind_zone_polygons__(buildings, scaling_factor):
wind_zone_a = DXFHelper.__generate_wind_zone_a_dual_tilt__(buildings, scaling_factor)
wind_zone_a += DXFHelper.__generate_wind_zone_a_fuzzy_dual_tilt__(buildings, scaling_factor)
polygons = []
for wind_zone in wind_zone_a:
polygon = Polygon(points=wind_zone)
polygon.color = "red"
polygons.append(polygon)
wind_zone_b = DXFHelper.__generate_wind_zone_b_dual_tilt__(buildings, scaling_factor)
wind_zone_b += DXFHelper.__generate_wind_zone_b_fuzzy_dual_tilt__(buildings, scaling_factor)
for wind_zone in wind_zone_b:
polygon = Polygon(points=wind_zone)
polygon.color = "green"
polygons.append(polygon)
wind_zone_c = DXFHelper.__generate_wind_zone_c_dual_tilt__(buildings, scaling_factor)
wind_zone_c += DXFHelper.__generate_wind_zone_c_fuzzy_dual_tilt__(buildings, scaling_factor)
for wind_zone in wind_zone_c:
polygon = Polygon(points=wind_zone)
polygon.color = "blue"
polygons.append(polygon)
wind_zone_d = DXFHelper.__generate_wind_zone_d_dual_tilt__(buildings, scaling_factor)
wind_zone_d += DXFHelper.__generate_wind_zone_d_fuzzy_dual_tilt__(buildings, scaling_factor)
for wind_zone in wind_zone_d:
polygon = Polygon(points=wind_zone)
polygon.color = "black"
polygons.append(polygon)
return reversed(polygons)
@staticmethod
def __single_tilt_wind_zone_polygons__(buildings, scaling_factor, panel_orientation):
wind_zone_a = DXFHelper.__generate_wind_zone_a_single_tilt__(buildings, scaling_factor, panel_orientation)
wind_zone_a += DXFHelper.__generate_wind_zone_a_fuzzy_single_tilt__(buildings, scaling_factor)
polygons = []
for wind_zone in wind_zone_a:
polygon = Polygon(points=wind_zone)
polygon.color = "red"
polygons.append(polygon)
wind_zone_i = DXFHelper.__generate_wind_zone_i_single_tilt__(buildings, scaling_factor, panel_orientation)
for wind_zone in wind_zone_i:
polygon = Polygon(points=wind_zone)
polygon.color = "orange"
polygons.append(polygon)
wind_zone_j = DXFHelper.__generate_wind_zone_j_single_tilt__(buildings, scaling_factor, panel_orientation)
for wind_zone in wind_zone_j:
polygon = Polygon(points=wind_zone)
polygon.color = "forestgreen"
polygons.append(polygon)
wind_zone_b = DXFHelper.__generate_wind_zone_b_single_tilt__(buildings, scaling_factor, panel_orientation)
wind_zone_b += DXFHelper.__generate_wind_zone_b_fuzzy_single_tilt__(buildings, scaling_factor)
for wind_zone in wind_zone_b:
polygon = Polygon(points=wind_zone)
polygon.color = "green"
polygons.append(polygon)
wind_zone_h = DXFHelper.__generate_wind_zone_h_single_tilt__(buildings, scaling_factor, panel_orientation)
for wind_zone in wind_zone_h:
polygon = Polygon(points=wind_zone)
polygon.color = "yellow"
polygons.append(polygon)
wind_zone_c = DXFHelper.__generate_wind_zone_c_single_tilt__(buildings, scaling_factor, panel_orientation)
wind_zone_c += DXFHelper.__generate_wind_zone_c_fuzzy_single_tilt__(buildings, scaling_factor)
for wind_zone in wind_zone_c:
polygon = Polygon(points=wind_zone)
polygon.color = "blue"
polygons.append(polygon)
wind_zone_e = DXFHelper.__generate_wind_zone_e_single_tilt__(buildings, scaling_factor, panel_orientation)
for wind_zone in wind_zone_e:
polygon = Polygon(points=wind_zone)
polygon.color = "purple"
polygons.append(polygon)
wind_zone_f = DXFHelper.__generate_wind_zone_f_single_tilt__(buildings, scaling_factor, panel_orientation)
for wind_zone in wind_zone_f:
polygon = Polygon(points=wind_zone)
polygon.color = "grey"
polygons.append(polygon)
wind_zone_d = DXFHelper.__generate_wind_zone_d_single_tilt__(buildings, scaling_factor, panel_orientation)
wind_zone_d += DXFHelper.__generate_wind_zone_d_fuzzy_single_tilt__(buildings, scaling_factor)
for wind_zone in wind_zone_d:
polygon = Polygon(points=wind_zone)
polygon.color = "black"
polygons.append(polygon)
wind_zone_g = DXFHelper.__generate_wind_zone_g_single_tilt__(buildings, scaling_factor, panel_orientation)
for wind_zone in wind_zone_g:
polygon = Polygon(points=wind_zone)
polygon.color = "hotpink"
polygons.append(polygon)
return reversed(polygons)