diff --git a/bpeng/__init__.py b/bpeng/__init__.py index d7ad9365b97c0fd0e0f4496c60cf262a91535c95..76ce1c00636a1f47a842b3ab8eced04e317fbdd8 100644 --- a/bpeng/__init__.py +++ b/bpeng/__init__.py @@ -1 +1,2 @@ from .heatloss.heatloss import HeatLoss +from .dimensions.parse_dimensions import ParseDimensions diff --git a/bpeng/dimensions/__init__.py b/bpeng/dimensions/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/bpeng/dimensions/parse_dimensions.py b/bpeng/dimensions/parse_dimensions.py new file mode 100644 index 0000000000000000000000000000000000000000..c5234aece630f276e94e95bffcd7ce163cc53dd3 --- /dev/null +++ b/bpeng/dimensions/parse_dimensions.py @@ -0,0 +1,190 @@ +""" +Parse a dimensions file +""" + +import xlrd + +class ParseDimensions: + """ + Class to parse a dimensions file + """ + + DIRECTION_DICT = { + 'South': 1, 'south': 1, 'front': 1, + 'East': 2, 'east': 2, 'right': 2, + 'West': 3, 'west': 3, 'left': 3, + 'North': 4, 'north': 4, 'back': 4, + } + + FEATURE_DICT = { + 'Door': 1, + 'Window': 2, + 'Building Point': 3, + 'Roof Point': 4, + } + + @staticmethod + def parse(dimensions_file): + """ + Parse a dimensions file + Relies on specific formatting + + Args: + + dimensions_file (bytes): The file to parse + + Returns: + + windows_doors (array) An array of dicts representing + windows doors + points (array) An array of dicts representing + points + building_dimensions (dict) A dict repesenting + the data that is building specific + + """ + workbook = xlrd.open_workbook(file_contents=dimensions_file) + worksheet = workbook.sheet_by_index(0) + + perimeter = worksheet.cell(4, 1).value + area = worksheet.cell(5, 1).value + num_floors = worksheet.cell(4, 3).value + ground_elevation = worksheet.cell(5, 3).value + north_adjacency = worksheet.cell(5, 16).value + south_adjacency = worksheet.cell(5, 22).value + west_adjacency = worksheet.cell(20, 16).value + east_adjacency = worksheet.cell(20, 22).value + + building_dimensions = { + 'perimeter': float(perimeter), + 'area': float(area), + 'num_floors': int(num_floors), + 'ground_elevation': int(ground_elevation), + 'north_adjacency': north_adjacency, + 'south_adjacency': south_adjacency, + 'west_adjacency': west_adjacency, + 'east_adjacency': east_adjacency, + } + + point_list = [] + # Building and roof points have seperate starting places on the excel spreadsheet, + start_points = [ + { + 'row_start': 8, + 'column_start': 1, + 'feature': ParseDimensions.FEATURE_DICT['Building Point'], + }, + { + 'row_start': 14, + 'column_start': 7, + 'feature': ParseDimensions.FEATURE_DICT['Roof Point'], + }, + ] + # Get the building and roof points + for start_point in start_points: + row_start = start_point['row_start'] + column_start = start_point['column_start'] + row_counter = 0 + lat = worksheet.cell(row_start, column_start).value + while lat: + column_counter = 1 + longt = worksheet.cell( + row_start + row_counter, + column_start + column_counter + ).value + column_counter += 1 + elevation = worksheet.cell( + row_start + row_counter, + column_start + column_counter + ).value + column_counter += 1 + height = worksheet.cell( + row_start + row_counter, + column_start + column_counter + ).value + point_list.append( + { + 'latitude': float(lat), + 'longitude': float(longt), + 'elevation': int(elevation), + 'corresponding_height': int(height), + 'feature': start_point['feature'], + } + ) + row_counter += 1 + lat = worksheet.cell(row_start + row_counter, column_start).value + + windows_and_doors = [] + # Window and door data has many start points + start_points = [ + { + 'row_start': 7, + 'column_start': 12, + 'direction': ParseDimensions.DIRECTION_DICT['front'], + }, + { + 'row_start': 7, + 'column_start': 18, + 'direction': ParseDimensions.DIRECTION_DICT['back'], + }, + { + 'row_start': 22, + 'column_start': 12, + 'direction': ParseDimensions.DIRECTION_DICT['left'], + }, + { + 'row_start': 22, + 'column_start': 18, + 'direction': ParseDimensions.DIRECTION_DICT['right'], + }, + ] + # Get Window & Door data + for start_point in start_points: + row_start = start_point['row_start'] + column_start = start_point['column_start'] + row_counter = 0 + feature_string = worksheet.cell(row_start, column_start).value + while feature_string: + # The cell telling us whether or not it is a door or a window is 1 before height + if feature_string.startswith('Window'): + feature = ParseDimensions.FEATURE_DICT['Window'] + elif feature_string.startswith('Door'): + feature = ParseDimensions.FEATURE_DICT['Door'] + else: + # We shouldn't be in this row + break + column_counter = 1 + height = worksheet.cell( + row_start + row_counter, + column_start + column_counter + ).value + column_counter += 1 + width = worksheet.cell( + row_start + row_counter, + column_start + column_counter + ).value + column_counter += 1 + quantity = worksheet.cell( + row_start + row_counter, + column_start + column_counter + ).value + column_counter += 1 + orientation = worksheet.cell( + row_start + row_counter, + column_start + column_counter + ).value + if height: + windows_and_doors.append( + { + 'height': float(height), + 'width': float(width), + 'quantity': int(quantity), + 'orientation': ParseDimensions.DIRECTION_DICT[orientation], + 'feature': feature, + 'direction': start_point['direction'], + } + ) + row_counter += 1 + feature_string = worksheet.cell(row_start + row_counter, column_start).value + + return windows_and_doors, point_list, building_dimensions diff --git a/bpeng/tests/data/test_dimensions_parsing.xlsx b/bpeng/tests/data/test_dimensions_parsing.xlsx new file mode 100755 index 0000000000000000000000000000000000000000..f2a9f217d779625e48313f4d31766ea6e43b7b25 Binary files /dev/null and b/bpeng/tests/data/test_dimensions_parsing.xlsx differ diff --git a/bpeng/tests/test_dimensions.py b/bpeng/tests/test_dimensions.py new file mode 100644 index 0000000000000000000000000000000000000000..06d395871fe832da597f272ff15f72bafb5a9dbe --- /dev/null +++ b/bpeng/tests/test_dimensions.py @@ -0,0 +1,43 @@ +"""Test parse dimensions""" +import os +from bpeng import ParseDimensions + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + +class TestDimensions: + + def setup_class(self): + self.pd = ParseDimensions() + + def test_parse(self): + with open(os.path.join(BASE_DIR, 'data/test_dimensions_parsing.xlsx'), 'r+b') as f: + windows_doors, points, building_dimensions = self.pd.parse(f.read()) + + # Test points + assert len(points) == 13 + first_point = points[0] + assert first_point['elevation'] == 713 + assert first_point['longitude'] == -85.281127 + assert first_point['latitude'] == 35.034178 + assert first_point['corresponding_height'] == 12 + assert first_point['feature'] == 3 + + # Test window_doors + assert len(windows_doors) == 9 + first_window_door = windows_doors[0] + assert first_window_door['feature'] == 2 + assert first_window_door['orientation'] == 4 + assert first_window_door['height'] == 5.0 + assert first_window_door['direction'] == 1 + assert first_window_door['width'] == 6.0 + assert first_window_door['quantity'] == 2 + + # Test building_dimensions + assert building_dimensions['num_floors'] == 1 + assert building_dimensions['ground_elevation'] == 701 + assert building_dimensions['area'] == 2348.23 + assert building_dimensions['perimeter'] == 213.29 + assert building_dimensions['north_adjacency'] == 0.5 + assert building_dimensions['south_adjacency'] == 0.0 + assert building_dimensions['west_adjacency'] == 1.0 + assert building_dimensions['east_adjacency'] == 0.0 diff --git a/requirements.txt b/requirements.txt index 2e46715eee6f96f9a3d4eff5c5b14caa25c99b5e..0b8b9686a0eccc658645248d63e45cfb1f4d8345 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ numpy==1.12.0 pandas==0.19.2 +xlrd==1.0.0